[PATCH] kernel32: implement condition variables
Marcus Meissner
marcus at jet.franken.de
Sat Jan 19 03:37:36 CST 2013
Hi,
This patch does fail the "Adobe Lightroom" testcase,
please disregard :(
Ciao, Marcus
On Fri, Jan 18, 2013 at 06:13:46PM +0100, Marcus Meissner wrote:
> Hi,
>
> 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 issue:
> We are busy waiting on the bit 0 lock.
>
> Ciao, Marcus
> ---
> dlls/kernel32/kernel32.spec | 4 +
> dlls/kernel32/sync.c | 174 +++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 178 insertions(+)
>
> diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
> index 0bd1adc..69de820 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)
> @@ -1187,6 +1188,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)
> @@ -1256,6 +1258,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..e1987bc 100644
> --- a/dlls/kernel32/sync.c
> +++ b/dlls/kernel32/sync.c
> @@ -2299,3 +2299,177 @@ __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 or access via bit 0 of variable->Ptr. This avoids needs for other global variables.
> + */
> +
> +struct _condvar_intern {
> + struct _condvar_intern *next;
> + HANDLE event;
> +};
> +
> +/**********************************************************************
> + * InitializeConditionVariable (KERNEL32.@)
> + */
> +VOID WINAPI InitializeConditionVariable(PCONDITION_VARIABLE variable)
> +{
> + variable->Ptr = NULL;
> +}
> +
> +/**********************************************************************
> + * WakeConditionVariable (KERNEL32.@)
> + */
> +VOID WINAPI WakeConditionVariable(PCONDITION_VARIABLE variable)
> +{
> + struct _condvar_intern *oldvar, *var;
> +
> + /*FIXME("(%p) aka %p\n", variable, var);*/
> +
> + /* Get a lock on the list head */
> + do {
> + __asm__ __volatile__("":::"memory");
> + var = variable->Ptr;
> + if (!var) return; /* Nothing to do */
> + if ((DWORD_PTR)var & 1) continue;/* Need to busy wait a bit. */
> +
> + oldvar = InterlockedCompareExchangePointer (&variable->Ptr, (void*)((DWORD_PTR)var|1), var);
> + if (oldvar == var)
> + break;
> + } while (1);
> +
> + NtSetEvent (var->event, NULL);
> + var->event = NULL;
> +
> + variable->Ptr = var->next; /* unlock the list head */
> +}
> +
> +/**********************************************************************
> + * WakeAllConditionVariable (KERNEL32.@)
> + */
> +VOID WINAPI WakeAllConditionVariable(PCONDITION_VARIABLE variable)
> +{
> + struct _condvar_intern *oldvar, *var;
> +
> + /*FIXME("(%p) aka %p\n", variable, var);*/
> +
> + /* Get a lock on the list head */
> + do {
> + __asm__ __volatile__("":::"memory");
> + var = variable->Ptr;
> + if (!var) return; /* Nothing to do */
> + if ((DWORD_PTR)var & 1) continue; /* Need to busy wait a bit. */
> +
> + oldvar = InterlockedCompareExchangePointer (&variable->Ptr, (void*)1, var);
> + if (oldvar == var)
> + break;
> + /* failed locking, retry in busy loop. */
> + } while (1);
> +
> + /* dequeue whole waiter chain for wake up */
> + while (var) {
> + NtSetEvent (var->event, NULL);
> + var->event = NULL;
> + var = var->next;
> + } while (var);
> +
> + variable->Ptr = NULL; /* unlock list head */
> +}
> +
> +/**********************************************************************
> + * SleepConditionVariableCS (KERNEL32.@)
> + */
> +BOOL WINAPI SleepConditionVariableCS(PCONDITION_VARIABLE variable,
> + LPCRITICAL_SECTION crit, DWORD timeout
> +)
> +{
> + struct _condvar_intern myself;
> + struct _condvar_intern **xvar, *var, *oldvar;
> + BOOL ret = TRUE;
> + OBJECT_ATTRIBUTES attr;
> + HANDLE myevent;
> + DWORD res;
> +
> +
> + /*FIXME("(%p, %p, %d): semi-correct stub, var %p.\n", variable, crit, timeout, &myself);*/
> +
> + 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);
> + myevent = myself.event;
> + myself.next = NULL;
> + /* Get a lock on the list head */
> + do {
> + __asm__ __volatile__("":::"memory");
> + var = variable->Ptr;
> + if ((DWORD_PTR)var & 1) continue; /* Need to busy wait a bit. */
> +
> + oldvar = InterlockedCompareExchangePointer (&variable->Ptr, (void*)((DWORD_PTR)var|1), var);
> + if (oldvar == var)
> + break;
> + /* failed locking, retry in busy loop. */
> + } while (1);
> +
> + /* Attach ourselves to the end of the list */
> + xvar = (struct _condvar_intern **)&variable->Ptr;
> + while (((DWORD_PTR)*xvar) & ~1) {
> + struct _condvar_intern *yvar = (struct _condvar_intern *)((DWORD_PTR)(*xvar)&~1);
> + xvar = &yvar->next;
> + }
> + *xvar = &myself;
> +
> + variable->Ptr = var; /* unlock list head */
> +
> + RtlLeaveCriticalSection(crit);
> +
> + res = WaitForSingleObject(myevent, timeout);
> +
> + /* Get a lock on the list head */
> + do {
> + __asm__ __volatile__("":::"memory");
> + var = variable->Ptr;
> + if (!var) break; /* Nothing to do */
> + if ((DWORD_PTR)var & 1) continue; /* Need to busy wait a bit. */
> +
> + oldvar = InterlockedCompareExchangePointer (&variable->Ptr, (void*)((DWORD_PTR)var|1), var);
> + if (oldvar == var)
> + break;
> + /* failed locking, retry in busy loop. */
> + } while (1);
> +
> + if (res == WAIT_TIMEOUT) {
> + xvar = (struct _condvar_intern **)&variable->Ptr;
> + while (((DWORD_PTR)*xvar) & ~1) {
> + struct _condvar_intern *yvar = (struct _condvar_intern *)((DWORD_PTR)(*xvar)&~1);
> + if (yvar == &myself) {
> + *xvar = yvar->next;
> + break;
> + }
> + xvar = &yvar->next;
> + }
> + /* If we did not overwrite the list head and so unlock the list,
> + * do it now. */
> + if ((DWORD_PTR)variable->Ptr & 1)
> + variable->Ptr = (void*)((DWORD_PTR)variable->Ptr & ~1);
> +
> + ret = FALSE;
> + /* expected from caller, but not set by WaitForSingleObject */
> + SetLastError(ERROR_TIMEOUT);
> + } else
> + variable->Ptr = var;
> +
> + RtlEnterCriticalSection(crit);
> + CloseHandle (myevent);
> +
> + return ret;
> +}
> --
> 1.7.10.4
>
>
>
>
More information about the wine-patches
mailing list