xref: /reactos/sdk/lib/rtl/runonce.c (revision 8a92b556)
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 WINAPI RtlRunOnceInitialize( RTL_RUN_ONCE *once )
12 {
13     once->Ptr = NULL;
14 }
15 
16 /******************************************************************
17  *              RtlRunOnceBeginInitialize (NTDLL.@)
18  */
19 DWORD WINAPI RtlRunOnceBeginInitialize( RTL_RUN_ONCE *once, ULONG flags, void **context )
20 {
21     if (flags & RTL_RUN_ONCE_CHECK_ONLY)
22     {
23         ULONG_PTR val = (ULONG_PTR)once->Ptr;
24 
25         if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
26         if ((val & 3) != 2) return STATUS_UNSUCCESSFUL;
27         if (context) *context = (void *)(val & ~3);
28         return STATUS_SUCCESS;
29     }
30 
31     for (;;)
32     {
33         ULONG_PTR next, val = (ULONG_PTR)once->Ptr;
34 
35         switch (val & 3)
36         {
37         case 0:  /* first time */
38             if (!interlocked_cmpxchg_ptr( &once->Ptr,
39                                           (flags & RTL_RUN_ONCE_ASYNC) ? (void *)3 : (void *)1, 0 ))
40                 return STATUS_PENDING;
41             break;
42 
43         case 1:  /* in progress, wait */
44             if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
45             next = val & ~3;
46             if (interlocked_cmpxchg_ptr( &once->Ptr, (void *)((ULONG_PTR)&next | 1),
47                                          (void *)val ) == (void *)val)
48                 NtWaitForKeyedEvent( 0, &next, FALSE, NULL );
49             break;
50 
51         case 2:  /* done */
52             if (context) *context = (void *)(val & ~3);
53             return STATUS_SUCCESS;
54 
55         case 3:  /* in progress, async */
56             if (!(flags & RTL_RUN_ONCE_ASYNC)) return STATUS_INVALID_PARAMETER;
57             return STATUS_PENDING;
58         }
59     }
60 }
61 
62 /******************************************************************
63  *              RtlRunOnceComplete (NTDLL.@)
64  */
65 DWORD WINAPI RtlRunOnceComplete( RTL_RUN_ONCE *once, ULONG flags, void *context )
66 {
67     if ((ULONG_PTR)context & 3) return STATUS_INVALID_PARAMETER;
68 
69     if (flags & RTL_RUN_ONCE_INIT_FAILED)
70     {
71         if (context) return STATUS_INVALID_PARAMETER;
72         if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
73     }
74     else context = (void *)((ULONG_PTR)context | 2);
75 
76     for (;;)
77     {
78         ULONG_PTR val = (ULONG_PTR)once->Ptr;
79 
80         switch (val & 3)
81         {
82         case 1:  /* in progress */
83             if (interlocked_cmpxchg_ptr( &once->Ptr, context, (void *)val ) != (void *)val) break;
84             val &= ~3;
85             while (val)
86             {
87                 ULONG_PTR next = *(ULONG_PTR *)val;
88                 NtReleaseKeyedEvent( 0, (void *)val, FALSE, NULL );
89                 val = next;
90             }
91             return STATUS_SUCCESS;
92 
93         case 3:  /* in progress, async */
94             if (!(flags & RTL_RUN_ONCE_ASYNC)) return STATUS_INVALID_PARAMETER;
95             if (interlocked_cmpxchg_ptr( &once->Ptr, context, (void *)val ) != (void *)val) break;
96             return STATUS_SUCCESS;
97 
98         default:
99             return STATUS_UNSUCCESSFUL;
100         }
101     }
102 }
103 
104 /******************************************************************
105  *              RtlRunOnceExecuteOnce (NTDLL.@)
106  */
107 DWORD WINAPI RtlRunOnceExecuteOnce( RTL_RUN_ONCE *once, PRTL_RUN_ONCE_INIT_FN func,
108                                     void *param, void **context )
109 {
110     DWORD ret = RtlRunOnceBeginInitialize( once, 0, context );
111 
112     if (ret != STATUS_PENDING) return ret;
113 
114     if (!func( once, param, context ))
115     {
116         RtlRunOnceComplete( once, RTL_RUN_ONCE_INIT_FAILED, NULL );
117         return STATUS_UNSUCCESSFUL;
118     }
119 
120     return RtlRunOnceComplete( once, 0, context ? *context : NULL );
121 }
122