1 2 /* Taken from Wine ntdll/sync.c */ 3 4 #include <rtl_vista.h> 5 #include <wine/config.h> 6 #include <wine/port.h> 7 8 /****************************************************************** 9 * RtlRunOnceInitialize (NTDLL.@) 10 */ 11 VOID NTAPI RtlRunOnceInitialize(_Out_ PRTL_RUN_ONCE RunOnce) 12 { 13 RunOnce->Ptr = NULL; 14 } 15 16 /****************************************************************** 17 * RtlRunOnceBeginInitialize (NTDLL.@) 18 */ 19 _Must_inspect_result_ 20 NTSTATUS 21 NTAPI 22 RtlRunOnceBeginInitialize( 23 _Inout_ PRTL_RUN_ONCE RunOnce, 24 _In_ ULONG Flags, 25 _Outptr_opt_result_maybenull_ PVOID *Context) 26 { 27 if (Flags & RTL_RUN_ONCE_CHECK_ONLY) 28 { 29 ULONG_PTR val = (ULONG_PTR)RunOnce->Ptr; 30 31 if (Flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER; 32 if ((val & 3) != 2) return STATUS_UNSUCCESSFUL; 33 if (Context) *Context = (void *)(val & ~3); 34 return STATUS_SUCCESS; 35 } 36 37 for (;;) 38 { 39 ULONG_PTR next, val = (ULONG_PTR)RunOnce->Ptr; 40 41 switch (val & 3) 42 { 43 case 0: /* first time */ 44 if (!interlocked_cmpxchg_ptr( &RunOnce->Ptr, 45 (Flags & RTL_RUN_ONCE_ASYNC) ? (void *)3 : (void *)1, 0)) 46 return STATUS_PENDING; 47 break; 48 49 case 1: /* in progress, wait */ 50 if (Flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER; 51 next = val & ~3; 52 if (interlocked_cmpxchg_ptr( &RunOnce->Ptr, (void *)((ULONG_PTR)&next | 1), 53 (void *)val ) == (void *)val) 54 NtWaitForKeyedEvent( 0, &next, FALSE, NULL ); 55 break; 56 57 case 2: /* done */ 58 if (Context) *Context = (void *)(val & ~3); 59 return STATUS_SUCCESS; 60 61 case 3: /* in progress, async */ 62 if (!(Flags & RTL_RUN_ONCE_ASYNC)) return STATUS_INVALID_PARAMETER; 63 return STATUS_PENDING; 64 } 65 } 66 } 67 68 /****************************************************************** 69 * RtlRunOnceComplete (NTDLL.@) 70 */ 71 NTSTATUS 72 NTAPI 73 RtlRunOnceComplete( 74 _Inout_ PRTL_RUN_ONCE RunOnce, 75 _In_ ULONG Flags, 76 _In_opt_ PVOID Context) 77 { 78 if ((ULONG_PTR)Context & 3) return STATUS_INVALID_PARAMETER; 79 80 if (Flags & RTL_RUN_ONCE_INIT_FAILED) 81 { 82 if (Context) return STATUS_INVALID_PARAMETER; 83 if (Flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER; 84 } 85 else Context = (void *)((ULONG_PTR)Context | 2); 86 87 for (;;) 88 { 89 ULONG_PTR val = (ULONG_PTR)RunOnce->Ptr; 90 91 switch (val & 3) 92 { 93 case 1: /* in progress */ 94 if (interlocked_cmpxchg_ptr( &RunOnce->Ptr, Context, (void *)val ) != (void *)val) break; 95 val &= ~3; 96 while (val) 97 { 98 ULONG_PTR next = *(ULONG_PTR *)val; 99 NtReleaseKeyedEvent( 0, (void *)val, FALSE, NULL ); 100 val = next; 101 } 102 return STATUS_SUCCESS; 103 104 case 3: /* in progress, async */ 105 if (!(Flags & RTL_RUN_ONCE_ASYNC)) return STATUS_INVALID_PARAMETER; 106 if (interlocked_cmpxchg_ptr( &RunOnce->Ptr, Context, (void *)val) != (void *)val) break; 107 return STATUS_SUCCESS; 108 109 default: 110 return STATUS_UNSUCCESSFUL; 111 } 112 } 113 } 114 115 /****************************************************************** 116 * RtlRunOnceExecuteOnce (NTDLL.@) 117 */ 118 _Maybe_raises_SEH_exception_ 119 NTSTATUS 120 NTAPI 121 RtlRunOnceExecuteOnce( 122 _Inout_ PRTL_RUN_ONCE RunOnce, 123 _In_ __inner_callback PRTL_RUN_ONCE_INIT_FN InitFn, 124 _Inout_opt_ PVOID Parameter, 125 _Outptr_opt_result_maybenull_ PVOID *Context) 126 { 127 DWORD ret = RtlRunOnceBeginInitialize( RunOnce, 0, Context ); 128 129 if (ret != STATUS_PENDING) return ret; 130 131 if (!InitFn( RunOnce, Parameter, Context )) 132 { 133 RtlRunOnceComplete( RunOnce, RTL_RUN_ONCE_INIT_FAILED, NULL ); 134 return STATUS_UNSUCCESSFUL; 135 } 136 137 return RtlRunOnceComplete( RunOnce, 0, Context ? *Context : NULL ); 138 } 139