There are only two types of systems we care about: Windows, and variations of pthreads. Other systems can be dealt with by people who care about them. Windows ------- On Windows, mutexes cannot be initialized statically, but on the other hand, we are guaranteed to have atomic primitives available without a mutex. This means mutexes can be initialized dynamically with something like this: #define PIXMAN_DEFINE_LOCK(name) \ static CRITICAL_SECTION __ ## name ## _critical_section; \ static CRITICAL_SECTION *__ ## name ## _ptr; #define PIXMAN_LOCK(name) \ if (!PIXMAN_ATOMIC_POINTER_GET ((void **)&__ ## name ## _ptr)) \ { \ _pixman_win32_initialize_critical_section ( \ &__ ## name ## _ptr, \ &__ ## name ## _critical_section); \ } \ \ EnterCriticalSection(&__ ## name ## _critical_section); #define PIXMAN_UNLOCK(name) \ LeaveCriticalSection(&__ ## name ## _critical_section); Where _pixman_win32_initialize_critical_section() would be responsible for initializing the variables. (And not doing anything if they are already set). Pthreads -------- On pthreads, we may or may not have atomic primitives, but mutexes can always be initialized statically. That means we can fake atomic primitives with mutexes if necessary. Ie,. something like #define PIXMAN_DEFINE_LOCK(name) \ pthread_mutex_t __ ## name ## __lock = pthread_mutex_initializer; #ifdef BUILTIN_ATOMICS ... #else /* fake atomic primitives with mutexes */ #endif Therefore, we can guarantee availability of the following macros: PIXMAN_ATOMIC_POINTER_GET(void **) PIXMAN_ATOMIC_POINTER_SET(void **, void *value) PIXMAN_ATOMIC_POINTER_CMPXCHG() PIXMAN_ATOMIC_INT_GET(atomic *a) PIXMAN_ATOMIC_INT_SET(atomic *a, int value) PIXMAN_ATOMIC_INT_DEC_AND_TEST(atomic *a) PIXMAN_ATOMIC_INT_CMPXCHG(atomic, ...) PIXMAN_MUTEX_DEFINE_STATIC(name) PIXMAN_MUTEX_LOCK(name) PIXMAN_MUTEX_UNLOCK(name)