[PATCH] kernel32: conditional variable support (try 6)
Marcus Meissner
marcus at jet.franken.de
Wed Jan 30 04:20:03 CST 2013
Hi,
Additional tightening and cleaning of comments.
After Alexandres last guidance ("no global allocation")
and some hours of hair pulling I now did a implementation
with just a linked list of waiters.
variable->Ptr points to the head of this list.
The bit 0 of variable->Ptr is used for atomic locking of
the list and also its content, avoiding need for a lock variable.
Small issues:
We are busy waiting on the bit 0 lock, not the most performance
wise best thing.
Ciao, Marcus
---
dlls/kernel32/kernel32.spec | 4 ++
dlls/kernel32/sync.c | 151 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 155 insertions(+)
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index f2f039b..4bfbceb 100644
--- a/dlls/kernel32/kernel32.spec
+++ b/dlls/kernel32/kernel32.spec
@@ -745,6 +745,7 @@
@ stdcall IdnToUnicode(long wstr long ptr long)
@ stdcall InitAtomTable(long)
@ stdcall InitializeSRWLock(ptr)
+@ stdcall InitializeConditionVariable(ptr)
@ stdcall InitializeCriticalSection(ptr)
@ stdcall InitializeCriticalSectionAndSpinCount(ptr long)
@ stdcall InitializeCriticalSectionEx(ptr long long)
@@ -1188,6 +1189,7 @@
@ stdcall SignalObjectAndWait(long long long long)
@ stdcall SizeofResource(long long)
@ stdcall Sleep(long)
+@ stdcall SleepConditionVariableCS(ptr ptr long)
@ stdcall SleepEx(long long)
@ stdcall SuspendThread(long)
@ stdcall SwitchToFiber(ptr)
@@ -1257,6 +1259,8 @@
@ stdcall WaitForSingleObjectEx(long long long)
@ stdcall WaitNamedPipeA (str long)
@ stdcall WaitNamedPipeW (wstr long)
+@ stdcall WakeAllConditionVariable (ptr)
+@ stdcall WakeConditionVariable (ptr)
@ stdcall WerRegisterFile(wstr long long)
@ stdcall WerRegisterMemoryBlock(ptr long)
@ stdcall WerRegisterRuntimeExceptionModule(wstr ptr)
diff --git a/dlls/kernel32/sync.c b/dlls/kernel32/sync.c
index f8c951b..441c54c 100644
--- a/dlls/kernel32/sync.c
+++ b/dlls/kernel32/sync.c
@@ -2299,3 +2299,154 @@ __ASM_STDCALL_FUNC(InterlockedDecrement, 4,
"ret $4")
#endif /* __i386__ */
+
+/* See http://locklessinc.com/articles/mutex_cv_futex/ for a futex implementation
+ * See http://www.cs.wustl.edu/~schmidt/win32-cv-1.html for various implementations
+ * of pthread_cond_* with Win32 primitives.
+ * See glibc/nptl/DESIGN-condvar.txt for the glibc implementation
+ *
+ * This implementation uses a linked list as waiter queue, and the list
+ * and its content is locked for access via bit 0 of variable->Ptr.
+ * The actual waiting is done using a Win32 Event.
+ */
+
+struct _condvar_intern {
+ struct _condvar_intern *next;
+ HANDLE event;
+};
+
+/**********************************************************************
+ * InitializeConditionVariable (KERNEL32.@)
+ */
+VOID WINAPI InitializeConditionVariable(PCONDITION_VARIABLE variable)
+{
+ variable->Ptr = NULL;
+}
+
+/*
+ * We lock the condition variable by setting bit 0 in variable->Ptr
+ * atomically. We busy loop until we succeed and return the current value.
+ * We also lock the empty list (NULL).
+ *
+ * List unlocking is done with "variable->Ptr = <retvalue>"
+ */
+static struct _condvar_intern*
+_lock_list (PCONDITION_VARIABLE variable) {
+ struct _condvar_intern *oldvar, *var;
+
+ do {
+ __asm__ __volatile__("":::"memory");
+ var = variable->Ptr;
+ if ((DWORD_PTR)var & 1) continue; /* locked already, retry. */
+
+ oldvar = InterlockedCompareExchangePointer (&variable->Ptr, (void*)((DWORD_PTR)var|1), var);
+ if (oldvar == var)
+ break;
+ /* failed atomic locking, retry. */
+ } while (1);
+ return var;
+}
+
+/**********************************************************************
+ * WakeConditionVariable (KERNEL32.@)
+ */
+VOID WINAPI WakeConditionVariable(PCONDITION_VARIABLE variable)
+{
+ struct _condvar_intern *var;
+
+ if (!variable->Ptr)
+ return;
+
+ var = _lock_list (variable);
+ if (!var) {
+ variable->Ptr = NULL;
+ return;
+ }
+
+ /* remove and wake up the first entry */
+ NtSetEvent (var->event, NULL);
+
+ variable->Ptr = var->next;
+}
+
+/**********************************************************************
+ * WakeAllConditionVariable (KERNEL32.@)
+ */
+VOID WINAPI WakeAllConditionVariable(PCONDITION_VARIABLE variable)
+{
+ struct _condvar_intern *var;
+
+ if (!variable->Ptr)
+ return;
+
+ var = _lock_list (variable);
+
+ /* dequeue the whole waiter chain and wake it up */
+ while (var) {
+ NtSetEvent (var->event, NULL);
+ var = var->next;
+ }
+
+ variable->Ptr = NULL;
+}
+
+/**********************************************************************
+ * SleepConditionVariableCS (KERNEL32.@)
+ */
+BOOL WINAPI SleepConditionVariableCS(PCONDITION_VARIABLE variable,
+ LPCRITICAL_SECTION crit, DWORD timeout
+)
+{
+ struct _condvar_intern myself;
+ struct _condvar_intern **xvar, *var;
+ BOOL ret = TRUE;
+ OBJECT_ATTRIBUTES attr;
+ DWORD res;
+
+ attr.Length = sizeof(attr);
+ attr.RootDirectory = 0;
+ attr.Attributes = OBJ_INHERIT;
+ attr.ObjectName = NULL;
+ attr.SecurityDescriptor = NULL;
+ attr.SecurityQualityOfService = NULL;
+
+ NtCreateEvent (&myself.event, EVENT_ALL_ACCESS, &attr, NotificationEvent, 0);
+ myself.next = NULL;
+
+ var = _lock_list (variable);
+ /* Attach our stack based struct to the end of the list.
+ * (at the end for fairness)
+ */
+ xvar = &var;
+ while (*xvar)
+ xvar = &(*xvar)->next;
+ *xvar = &myself;
+ variable->Ptr = var;
+
+ RtlLeaveCriticalSection(crit);
+
+ res = WaitForSingleObject(myself.event, timeout);
+
+ var = _lock_list (variable);
+
+ /* Remove our entry */
+ xvar = (struct _condvar_intern **)&var;
+ while (*xvar) {
+ if (*xvar == &myself) {
+ *xvar = (*xvar)->next;
+ break;
+ }
+ xvar = &(*xvar)->next;
+ }
+ variable->Ptr = var;
+
+ CloseHandle (myself.event);
+ RtlEnterCriticalSection(crit);
+
+ if (res == WAIT_TIMEOUT) {
+ ret = FALSE;
+ /* expected from caller, but not set by WaitForSingleObject */
+ SetLastError(ERROR_TIMEOUT);
+ }
+ return ret;
+}
--
1.7.10.4
More information about the wine-patches
mailing list