[PATCH 12/21] kernel32: conditional variable support (try 6)
Marcus Meissner
marcus at jet.franken.de
Wed Jan 1 12:53:13 CST 2014
Hmm,
THis should have been patch 0021 instead of 0012 , please disregard.
Ciao, Marcus
On Wed, Jan 01, 2014 at 07:44:16PM +0100, Marcus Meissner wrote:
> 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 7779a75..e8d2bf0 100644
> --- a/dlls/kernel32/kernel32.spec
> +++ b/dlls/kernel32/kernel32.spec
> @@ -750,6 +750,7 @@
> @ stdcall InitOnceComplete(ptr long ptr)
> @ stdcall InitOnceExecuteOnce(ptr ptr ptr ptr)
> @ stdcall InitOnceInitialize(ptr) ntdll.RtlRunOnceInitialize
> +@ stdcall InitializeConditionVariable(ptr)
> @ stdcall InitializeCriticalSection(ptr)
> @ stdcall InitializeCriticalSectionAndSpinCount(ptr long)
> @ stdcall InitializeCriticalSectionEx(ptr long long)
> @@ -1194,6 +1195,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)
> @@ -1263,6 +1265,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 3c9e42a..34f39cd 100644
> --- a/dlls/kernel32/sync.c
> +++ b/dlls/kernel32/sync.c
> @@ -2327,3 +2327,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-devel
mailing list