mirror of
https://github.com/reactos/reactos.git
synced 2024-11-03 05:18:55 +00:00
119 lines
3.5 KiB
C
119 lines
3.5 KiB
C
|
|
||
|
#include "k32_vista.h"
|
||
|
|
||
|
#include <ndk/exfuncs.h>
|
||
|
#include <wine/config.h>
|
||
|
#include <wine/port.h>
|
||
|
|
||
|
/* Taken from Wine ntdll/sync.c */
|
||
|
|
||
|
HANDLE keyed_event = NULL;
|
||
|
|
||
|
static DWORD WINAPI RtlRunOnceBeginInitialize( RTL_RUN_ONCE *once, ULONG flags, void **context )
|
||
|
{
|
||
|
if (flags & RTL_RUN_ONCE_CHECK_ONLY)
|
||
|
{
|
||
|
ULONG_PTR val = (ULONG_PTR)once->Ptr;
|
||
|
|
||
|
if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
|
||
|
if ((val & 3) != 2) return STATUS_UNSUCCESSFUL;
|
||
|
if (context) *context = (void *)(val & ~3);
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
ULONG_PTR next, val = (ULONG_PTR)once->Ptr;
|
||
|
|
||
|
switch (val & 3)
|
||
|
{
|
||
|
case 0: /* first time */
|
||
|
if (!interlocked_cmpxchg_ptr( &once->Ptr,
|
||
|
(flags & RTL_RUN_ONCE_ASYNC) ? (void *)3 : (void *)1, 0 ))
|
||
|
return STATUS_PENDING;
|
||
|
break;
|
||
|
|
||
|
case 1: /* in progress, wait */
|
||
|
if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
|
||
|
next = val & ~3;
|
||
|
if (interlocked_cmpxchg_ptr( &once->Ptr, (void *)((ULONG_PTR)&next | 1),
|
||
|
(void *)val ) == (void *)val)
|
||
|
NtWaitForKeyedEvent( keyed_event, &next, FALSE, NULL );
|
||
|
break;
|
||
|
|
||
|
case 2: /* done */
|
||
|
if (context) *context = (void *)(val & ~3);
|
||
|
return STATUS_SUCCESS;
|
||
|
|
||
|
case 3: /* in progress, async */
|
||
|
if (!(flags & RTL_RUN_ONCE_ASYNC)) return STATUS_INVALID_PARAMETER;
|
||
|
return STATUS_PENDING;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static DWORD WINAPI RtlpRunOnceComplete( RTL_RUN_ONCE *once, ULONG flags, void *context )
|
||
|
{
|
||
|
if ((ULONG_PTR)context & 3) return STATUS_INVALID_PARAMETER;
|
||
|
|
||
|
if (flags & RTL_RUN_ONCE_INIT_FAILED)
|
||
|
{
|
||
|
if (context) return STATUS_INVALID_PARAMETER;
|
||
|
if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
else context = (void *)((ULONG_PTR)context | 2);
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
ULONG_PTR val = (ULONG_PTR)once->Ptr;
|
||
|
|
||
|
switch (val & 3)
|
||
|
{
|
||
|
case 1: /* in progress */
|
||
|
if (interlocked_cmpxchg_ptr( &once->Ptr, context, (void *)val ) != (void *)val) break;
|
||
|
val &= ~3;
|
||
|
while (val)
|
||
|
{
|
||
|
ULONG_PTR next = *(ULONG_PTR *)val;
|
||
|
NtReleaseKeyedEvent( keyed_event, (void *)val, FALSE, NULL );
|
||
|
val = next;
|
||
|
}
|
||
|
return STATUS_SUCCESS;
|
||
|
|
||
|
case 3: /* in progress, async */
|
||
|
if (!(flags & RTL_RUN_ONCE_ASYNC)) return STATUS_INVALID_PARAMETER;
|
||
|
if (interlocked_cmpxchg_ptr( &once->Ptr, context, (void *)val ) != (void *)val) break;
|
||
|
return STATUS_SUCCESS;
|
||
|
|
||
|
default:
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static DWORD WINAPI RtlpRunOnceExecuteOnce( RTL_RUN_ONCE *once, PRTL_RUN_ONCE_INIT_FN func,
|
||
|
void *param, void **context )
|
||
|
{
|
||
|
DWORD ret = RtlRunOnceBeginInitialize( once, 0, context );
|
||
|
|
||
|
if (ret != STATUS_PENDING) return ret;
|
||
|
|
||
|
if (!func( once, param, context ))
|
||
|
{
|
||
|
RtlpRunOnceComplete( once, RTL_RUN_ONCE_INIT_FAILED, NULL );
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
return RtlpRunOnceComplete( once, 0, context ? *context : NULL );
|
||
|
}
|
||
|
|
||
|
/* Taken from Wine kernel32/sync.c */
|
||
|
|
||
|
/*
|
||
|
* @implemented
|
||
|
*/
|
||
|
BOOL NTAPI InitOnceExecuteOnce( INIT_ONCE *once, PINIT_ONCE_FN func, void *param, void **context )
|
||
|
{
|
||
|
return !RtlpRunOnceExecuteOnce( once, (PRTL_RUN_ONCE_INIT_FN)func, param, context );
|
||
|
}
|