6 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 * This file is part of VirtualBox base platform packages, as
9 * available from https://www.virtualbox.org.
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation, in version 3 of the
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 * The contents of this file may alternatively be used under the terms
25 * of the Common Development and Distribution License Version 1.0
26 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
27 * in the VirtualBox distribution, in which case the provisions of the
28 * CDDL are applicable instead of those of the GPL.
30 * You may elect to license modified versions of this file under the
31 * terms and conditions of either the GPL or the CDDL or both.
33 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
36 #ifndef IPRT_INCLUDED_once_h
37 #define IPRT_INCLUDED_once_h
38 #ifndef RT_WITHOUT_PRAGMA_ONCE
42 #include <iprt/cdefs.h>
43 #include <iprt/types.h>
45 #include <iprt/errcore.h>
46 #include <iprt/list.h>
50 /** @defgroup grp_rt_once RTOnce - Execute Once
56 * Callback that gets executed once.
58 * @returns IPRT style status code, RTOnce returns this.
60 * @param pvUser The user parameter.
62 typedef DECLCALLBACKTYPE(int32_t, FNRTONCE
,(void *pvUser
));
63 /** Pointer to a FNRTONCE. */
64 typedef FNRTONCE
*PFNRTONCE
;
67 * Callback that gets executed on IPRT/process termination.
69 * @param pvUser The user parameter.
70 * @param fLazyCleanUpOk Indicates whether lazy clean-up is OK (see
73 typedef DECLCALLBACKTYPE(void, FNRTONCECLEANUP
,(void *pvUser
, bool fLazyCleanUpOk
));
74 /** Pointer to a FNRTONCE. */
75 typedef FNRTONCECLEANUP
*PFNRTONCECLEANUP
;
78 * Execute once structure.
80 * This is typically a global variable that is statically initialized
81 * by RTONCE_INITIALIZER.
85 /** Event semaphore that the other guys are blocking on. */
86 RTSEMEVENTMULTI
volatile hEventMulti
;
87 /** Reference counter for hEventMulti. */
88 int32_t volatile cEventRefs
;
89 /** See RTONCESTATE. */
90 int32_t volatile iState
;
91 /** The return code of pfnOnce. */
94 /** Pointer to the clean-up function. */
95 PFNRTONCECLEANUP pfnCleanUp
;
96 /** Argument to hand to the clean-up function. */
98 /** Clean-up list entry. */
99 RTLISTNODE CleanUpNode
;
101 /** Pointer to a execute once struct. */
102 typedef RTONCE
*PRTONCE
;
105 * The execute once statemachine.
107 typedef enum RTONCESTATE
109 /** RTOnce() has not been called.
111 RTONCESTATE_UNINITIALIZED
= 1,
112 /** RTOnce() is busy, no race.
113 * Next: CREATING_SEM, DONE */
114 RTONCESTATE_BUSY_NO_SEM
,
115 /** More than one RTOnce() caller is busy.
116 * Next: BUSY_HAVE_SEM, BUSY_SPIN, DONE_CREATING_SEM, DONE */
117 RTONCESTATE_BUSY_CREATING_SEM
,
118 /** More than one RTOnce() caller, the first is busy, the others are
121 RTONCESTATE_BUSY_HAVE_SEM
,
122 /** More than one RTOnce() caller, the first is busy, the others failed to
123 * create a semaphore and are spinning.
125 RTONCESTATE_BUSY_SPIN
,
126 /** More than one RTOnce() caller, the first has completed, the others
127 * are busy creating the semaphore.
128 * Next: DONE_HAVE_SEM */
129 RTONCESTATE_DONE_CREATING_SEM
,
130 /** More than one RTOnce() caller, the first is busy grabbing the
131 * semaphore, while the others are waiting.
133 RTONCESTATE_DONE_HAVE_SEM
,
134 /** The execute once stuff has completed. */
135 RTONCESTATE_DONE
= 16
138 /** Static initializer for RTONCE variables. */
139 #define RTONCE_INITIALIZER \
140 { NIL_RTSEMEVENTMULTI, 0, RTONCESTATE_UNINITIALIZED, VERR_INTERNAL_ERROR, NULL, NULL, { NULL, NULL } }
144 * Serializes execution of the pfnOnce function, making sure it's
145 * executed exactly once and that nobody returns from RTOnce before
146 * it has executed successfully.
148 * @returns IPRT like status code returned by pfnOnce.
150 * @param pOnce Pointer to the execute once variable.
151 * @param pfnOnce The function to executed once.
152 * @param pfnCleanUp The function that will be doing the cleaning up.
154 * @param pvUser The user parameter for pfnOnce.
156 RTDECL(int) RTOnceSlow(PRTONCE pOnce
, PFNRTONCE pfnOnce
, FNRTONCECLEANUP pfnCleanUp
, void *pvUser
);
159 * Serializes execution of the pfnOnce function, making sure it's
160 * executed exactly once and that nobody returns from RTOnce before
161 * it has executed successfully.
163 * @returns IPRT like status code returned by pfnOnce.
165 * @param pOnce Pointer to the execute once variable.
166 * @param pfnOnce The function to executed once.
167 * @param pvUser The user parameter for pfnOnce.
169 DECLINLINE(int) RTOnce(PRTONCE pOnce
, PFNRTONCE pfnOnce
, void *pvUser
)
171 int32_t iState
= ASMAtomicUoReadS32(&pOnce
->iState
);
172 if (RT_LIKELY( iState
== RTONCESTATE_DONE
173 || iState
== RTONCESTATE_DONE_CREATING_SEM
174 || iState
== RTONCESTATE_DONE_HAVE_SEM
))
175 return ASMAtomicUoReadS32(&pOnce
->rc
);
176 return RTOnceSlow(pOnce
, pfnOnce
, NULL
, pvUser
);
180 * Execute pfnOnce once and register a termination clean-up callback.
182 * Serializes execution of the pfnOnce function, making sure it's
183 * executed exactly once and that nobody returns from RTOnce before
184 * it has executed successfully.
186 * @returns IPRT like status code returned by pfnOnce.
188 * @param pOnce Pointer to the execute once variable.
189 * @param pfnOnce The function to executed once.
190 * @param pfnCleanUp The function that will be doing the cleaning up.
191 * @param pvUser The user parameter for pfnOnce.
193 DECLINLINE(int) RTOnceEx(PRTONCE pOnce
, PFNRTONCE pfnOnce
, PFNRTONCECLEANUP pfnCleanUp
, void *pvUser
)
195 int32_t iState
= ASMAtomicUoReadS32(&pOnce
->iState
);
196 if (RT_LIKELY( iState
== RTONCESTATE_DONE
197 || iState
== RTONCESTATE_DONE_CREATING_SEM
198 || iState
== RTONCESTATE_DONE_HAVE_SEM
))
199 return ASMAtomicUoReadS32(&pOnce
->rc
);
200 return RTOnceSlow(pOnce
, pfnOnce
, pfnCleanUp
, pvUser
);
204 * Resets an execute once variable.
206 * The caller is responsible for making sure there are no concurrent accesses to
207 * the execute once variable.
209 * @param pOnce Pointer to the execute once variable.
211 RTDECL(void) RTOnceReset(PRTONCE pOnce
);
214 * Check whether the execute once variable was successfullly initialized.
216 DECLINLINE(bool) RTOnceWasInitialized(PRTONCE pOnce
)
218 int32_t const iState
= ASMAtomicUoReadS32(&pOnce
->iState
);
219 int32_t const rc
= ASMAtomicUoReadS32(&pOnce
->rc
);
220 return RT_SUCCESS(rc
)
221 && ( iState
== RTONCESTATE_DONE
222 || iState
== RTONCESTATE_DONE_CREATING_SEM
223 || iState
== RTONCESTATE_DONE_HAVE_SEM
);
230 #endif /* !IPRT_INCLUDED_once_h */