+/* {{{1 SRWLock and CONDITION_VARIABLE emulation (for Windows XP) */
+
+static DWORD g_thread_xp_waiter_tls;
+static CRITICAL_SECTION g_thread_xp_lock;
+
+/* {{{2 GThreadWaiter utility class for CONDITION_VARIABLE emulation */
+typedef struct _GThreadXpWaiter GThreadXpWaiter;
+struct _GThreadXpWaiter
+{
+ HANDLE event;
+ volatile GThreadXpWaiter *next;
+};
+
+static GThreadXpWaiter *
+g_thread_xp_waiter_get (void)
+{
+ GThreadXpWaiter *waiter;
+
+ waiter = TlsGetValue (g_thread_xp_waiter_tls);
+
+ if G_UNLIKELY (waiter == NULL)
+ {
+ waiter = malloc (sizeof (GThreadXpWaiter));
+ if (waiter == NULL)
+ g_thread_abort (GetLastError (), "malloc");
+ waiter->event = CreateEvent (0, FALSE, FALSE, NULL);
+ if (waiter->event == NULL)
+ g_thread_abort (GetLastError (), "CreateEvent");
+
+ TlsSetValue (g_thread_xp_waiter_tls, waiter);
+ }
+
+ return waiter;
+}
+
+static void
+g_thread_xp_CallThisOnThreadExit (void)
+{
+ GThreadXpWaiter *waiter;
+
+ waiter = TlsGetValue (g_thread_xp_waiter_tls);
+
+ if (waiter != NULL)
+ {
+ TlsSetValue (g_thread_xp_waiter_tls, NULL);
+ CloseHandle (waiter->event);
+ free (waiter);
+ }
+}
+
+/* {{{2 SRWLock emulation */
+typedef struct
+{
+ CRITICAL_SECTION critical_section;
+} GThreadSRWLock;
+
+static void
+g_thread_xp_InitializeSRWLock (gpointer mutex)
+{
+ *(GThreadSRWLock * volatile *) mutex = NULL;
+}
+
+static void
+g_thread_xp_DeleteSRWLock (gpointer mutex)
+{
+ GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex;
+
+ if (lock)
+ {
+ DeleteCriticalSection (&lock->critical_section);
+ free (lock);
+ }
+}
+
+static GThreadSRWLock *
+g_thread_xp_get_srwlock (GThreadSRWLock * volatile *lock)
+{
+ GThreadSRWLock *result;
+
+ /* It looks like we're missing some barriers here, but this code only
+ * ever runs on Windows XP, which in turn only ever runs on hardware
+ * with a relatively rigid memory model. The 'volatile' will take
+ * care of the compiler.
+ */
+ result = *lock;
+
+ if G_UNLIKELY (result == NULL)
+ {
+ EnterCriticalSection (&g_thread_xp_lock);
+
+ result = malloc (sizeof (GThreadSRWLock));
+
+ if (result == NULL)
+ g_thread_abort (errno, "malloc");
+
+ InitializeCriticalSection (&result->critical_section);
+ *lock = result;
+
+ LeaveCriticalSection (&g_thread_xp_lock);
+ }
+
+ return result;
+}
+
+static void
+g_thread_xp_AcquireSRWLockExclusive (gpointer mutex)
+{
+ GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
+
+ EnterCriticalSection (&lock->critical_section);
+}
+
+static BOOLEAN
+g_thread_xp_TryAcquireSRWLockExclusive (gpointer mutex)
+{
+ GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
+
+ return TryEnterCriticalSection (&lock->critical_section);
+}
+
+static void
+g_thread_xp_ReleaseSRWLockExclusive (gpointer mutex)
+{
+ GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex;
+
+ /* We need this until we fix some weird parts of GLib that try to
+ * unlock freshly-allocated mutexes.
+ */
+ if (lock != NULL)
+ LeaveCriticalSection (&lock->critical_section);
+}
+
+/* {{{2 CONDITION_VARIABLE emulation */
+typedef struct
+{
+ volatile GThreadXpWaiter *first;
+ volatile GThreadXpWaiter **last_ptr;
+} GThreadXpCONDITION_VARIABLE;
+
+static void
+g_thread_xp_InitializeConditionVariable (gpointer cond)
+{
+ *(GThreadXpCONDITION_VARIABLE * volatile *) cond = NULL;
+}
+
+static void
+g_thread_xp_DeleteConditionVariable (gpointer cond)
+{
+ GThreadXpCONDITION_VARIABLE *cv = *(GThreadXpCONDITION_VARIABLE * volatile *) cond;
+
+ if (cv)
+ free (cv);
+}
+
+static GThreadXpCONDITION_VARIABLE *
+g_thread_xp_get_condition_variable (GThreadXpCONDITION_VARIABLE * volatile *cond)
+{
+ GThreadXpCONDITION_VARIABLE *result;
+
+ /* It looks like we're missing some barriers here, but this code only
+ * ever runs on Windows XP, which in turn only ever runs on hardware
+ * with a relatively rigid memory model. The 'volatile' will take
+ * care of the compiler.
+ */
+ result = *cond;
+
+ if G_UNLIKELY (result == NULL)
+ {
+ result = malloc (sizeof (GThreadXpCONDITION_VARIABLE));
+
+ if (result == NULL)
+ g_thread_abort (errno, "malloc");
+
+ result->first = NULL;
+ result->last_ptr = &result->first;
+
+ if (InterlockedCompareExchangePointer (cond, result, NULL) != NULL)
+ {
+ free (result);
+ result = *cond;
+ }
+ }
+
+ return result;
+}
+
+static BOOL
+g_thread_xp_SleepConditionVariableSRW (gpointer cond,
+ gpointer mutex,
+ DWORD timeout,
+ ULONG flags)
+{
+ GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
+ GThreadXpWaiter *waiter = g_thread_xp_waiter_get ();
+ DWORD status;
+
+ waiter->next = NULL;
+
+ EnterCriticalSection (&g_thread_xp_lock);
+ *cv->last_ptr = waiter;
+ cv->last_ptr = &waiter->next;
+ LeaveCriticalSection (&g_thread_xp_lock);
+
+ g_mutex_unlock (mutex);
+ status = WaitForSingleObject (waiter->event, timeout);
+
+ if (status != WAIT_TIMEOUT && status != WAIT_OBJECT_0)
+ g_thread_abort (GetLastError (), "WaitForSingleObject");
+
+ g_mutex_lock (mutex);
+
+ return status == WAIT_OBJECT_0;
+}
+
+static void
+g_thread_xp_WakeConditionVariable (gpointer cond)
+{
+ GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
+ volatile GThreadXpWaiter *waiter;
+
+ EnterCriticalSection (&g_thread_xp_lock);
+ waiter = cv->first;
+ if (waiter != NULL)
+ {
+ cv->first = waiter->next;
+ if (cv->first == NULL)
+ cv->last_ptr = &cv->first;
+ }
+ LeaveCriticalSection (&g_thread_xp_lock);
+
+ if (waiter != NULL)
+ SetEvent (waiter->event);
+}
+
+static void
+g_thread_xp_WakeAllConditionVariable (gpointer cond)
+{
+ GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
+ volatile GThreadXpWaiter *waiter;
+
+ EnterCriticalSection (&g_thread_xp_lock);
+ waiter = cv->first;
+ cv->first = NULL;
+ cv->last_ptr = &cv->first;
+ LeaveCriticalSection (&g_thread_xp_lock);
+
+ while (waiter != NULL)
+ {
+ volatile GThreadXpWaiter *next;
+
+ next = waiter->next;
+ SetEvent (waiter->event);
+ waiter = next;
+ }
+}
+
+/* {{{2 XP Setup */
+static void
+g_thread_xp_init (void)
+{
+ static const GThreadImplVtable g_thread_xp_impl_vtable = {
+ g_thread_xp_CallThisOnThreadExit,
+ g_thread_xp_InitializeSRWLock,
+ g_thread_xp_DeleteSRWLock,
+ g_thread_xp_AcquireSRWLockExclusive,
+ g_thread_xp_TryAcquireSRWLockExclusive,
+ g_thread_xp_ReleaseSRWLockExclusive,
+ g_thread_xp_InitializeConditionVariable,
+ g_thread_xp_DeleteConditionVariable,
+ g_thread_xp_SleepConditionVariableSRW,
+ g_thread_xp_WakeAllConditionVariable,
+ g_thread_xp_WakeConditionVariable
+ };
+
+ InitializeCriticalSection (&g_thread_xp_lock);
+ g_thread_xp_waiter_tls = TlsAlloc ();
+
+ g_thread_impl_vtable = g_thread_xp_impl_vtable;
+}
+
+/* {{{1 Epilogue */
+