xref: /reactos/sdk/lib/rtl/runonce.c (revision f491d7cc)
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  */
RtlRunOnceInitialize(_Out_ PRTL_RUN_ONCE RunOnce)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
RtlRunOnceBeginInitialize(_Inout_ PRTL_RUN_ONCE RunOnce,_In_ ULONG Flags,_Outptr_opt_result_maybenull_ PVOID * Context)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
RtlRunOnceComplete(_Inout_ PRTL_RUN_ONCE RunOnce,_In_ ULONG Flags,_In_opt_ PVOID Context)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
RtlRunOnceExecuteOnce(_Inout_ PRTL_RUN_ONCE RunOnce,_In_ __inner_callback PRTL_RUN_ONCE_INIT_FN InitFn,_Inout_opt_ PVOID Parameter,_Outptr_opt_result_maybenull_ PVOID * Context)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