reactos/sdk/lib/rtl/runonce.c
Timo Kreuzer 61192390cf
[KERNEL32_VISTA][NTDLL_VISTA][RTL_VISTA] Move Vista Rtl functions from kernel32_vista and ntdll_vista to rtl_vista (#3149)
* Move RtlRunOnce functions from kernel32_vista to rtl_vista and export them from ntdll_vista
* Move condvar.c and srw.c from ntdll_vista to rtl_vista
* Move ntdll_vista build script to a subfolder of ntdll

The RtlRunOnce functions are taken from wine, completely unmodified.
The code that was in kernel32_vista had change that used a global keyed_event handle, but was never initialized, so we were still passing NULL thus using the global ExpCritSecOutOfMemoryEvent.
2020-09-12 15:04:02 +02:00

122 lines
3.7 KiB
C

/* Taken from Wine ntdll/sync.c */
#include "rtl_vista.h"
#include <wine/config.h>
#include <wine/port.h>
/******************************************************************
* RtlRunOnceInitialize (NTDLL.@)
*/
void WINAPI RtlRunOnceInitialize( RTL_RUN_ONCE *once )
{
once->Ptr = NULL;
}
/******************************************************************
* RtlRunOnceBeginInitialize (NTDLL.@)
*/
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( 0, &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;
}
}
}
/******************************************************************
* RtlRunOnceComplete (NTDLL.@)
*/
DWORD WINAPI RtlRunOnceComplete( 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( 0, (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;
}
}
}
/******************************************************************
* RtlRunOnceExecuteOnce (NTDLL.@)
*/
DWORD WINAPI RtlRunOnceExecuteOnce( 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 ))
{
RtlRunOnceComplete( once, RTL_RUN_ONCE_INIT_FAILED, NULL );
return STATUS_UNSUCCESSFUL;
}
return RtlRunOnceComplete( once, 0, context ? *context : NULL );
}