VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/once.cpp@ 37043

Last change on this file since 37043 was 33262, checked in by vboxsync, 14 years ago

RTOnce: Avoid allocating anything when there are no races.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.8 KB
Line 
1/* $Id: once.cpp 33262 2010-10-20 14:23:32Z vboxsync $ */
2/** @file
3 * IPRT - Execute Once.
4 */
5
6/*
7 * Copyright (C) 2007 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include <iprt/once.h>
32#include "internal/iprt.h"
33
34#include <iprt/semaphore.h>
35#include <iprt/thread.h>
36#include <iprt/err.h>
37#include <iprt/assert.h>
38#include <iprt/asm.h>
39
40
41/**
42 * The state loop of the other threads.
43 *
44 * @returns VINF_SUCCESS when everything went smoothly. IPRT status code if we
45 * encountered trouble.
46 * @param pOnce The execute once structure.
47 * @param phEvtM Where to store the semaphore handle so the caller
48 * can do the cleaning up for us.
49 */
50static int rtOnceOtherThread(PRTONCE pOnce, PRTSEMEVENTMULTI phEvtM)
51{
52 uint32_t cYields = 0;
53 for (;;)
54 {
55 int32_t iState = ASMAtomicReadS32(&pOnce->iState);
56 switch (iState)
57 {
58 /*
59 * No semaphore, try create one.
60 */
61 case RTONCESTATE_BUSY_NO_SEM:
62 if (ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_BUSY_CREATING_SEM, RTONCESTATE_BUSY_NO_SEM))
63 {
64 int rc = RTSemEventMultiCreate(phEvtM);
65 if (RT_SUCCESS(rc))
66 {
67 ASMAtomicWriteHandle(&pOnce->hEventMulti, *phEvtM);
68 int32_t cRefs = ASMAtomicIncS32(&pOnce->cEventRefs); Assert(cRefs == 1); NOREF(cRefs);
69
70 if (!ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_BUSY_HAVE_SEM, RTONCESTATE_BUSY_CREATING_SEM))
71 {
72 /* Too slow. */
73 AssertReturn(ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_DONE, RTONCESTATE_DONE_CREATING_SEM)
74 , VERR_INTERNAL_ERROR_5);
75
76 ASMAtomicWriteHandle(&pOnce->hEventMulti, NIL_RTSEMEVENTMULTI);
77 cRefs = ASMAtomicDecS32(&pOnce->cEventRefs); Assert(cRefs == 0);
78
79 RTSemEventMultiDestroy(*phEvtM);
80 *phEvtM = NIL_RTSEMEVENTMULTI;
81 }
82 }
83 else
84 {
85 AssertReturn( ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_BUSY_SPIN, RTONCESTATE_BUSY_CREATING_SEM)
86 || ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_DONE, RTONCESTATE_DONE_CREATING_SEM)
87 , VERR_INTERNAL_ERROR_4);
88 *phEvtM = NIL_RTSEMEVENTMULTI;
89 }
90 }
91 break;
92
93 /*
94 * This isn't nice, but it's the easy way out.
95 */
96 case RTONCESTATE_BUSY_CREATING_SEM:
97 case RTONCESTATE_BUSY_SPIN:
98 cYields++;
99 if (!(++cYields % 8))
100 RTThreadSleep(1);
101 else
102 RTThreadYield();
103 break;
104
105 /*
106 * There is a semaphore, try wait on it.
107 *
108 * We continue waiting after reaching DONE_HAVE_SEM if we
109 * already got the semaphore to avoid racing the first thread.
110 */
111 case RTONCESTATE_DONE_HAVE_SEM:
112 if (*phEvtM == NIL_RTSEMEVENTMULTI)
113 return VINF_SUCCESS;
114 /* fall thru */
115 case RTONCESTATE_BUSY_HAVE_SEM:
116 {
117 /*
118 * Grab the semaphore if we haven't got it yet.
119 * We must take care not to increment the counter if it
120 * is 0. This may happen if we're racing a state change.
121 */
122 if (*phEvtM == NIL_RTSEMEVENTMULTI)
123 {
124 int32_t cEventRefs = ASMAtomicUoReadS32(&pOnce->cEventRefs);
125 while ( cEventRefs > 0
126 && ASMAtomicUoReadS32(&pOnce->iState) == RTONCESTATE_BUSY_HAVE_SEM)
127 {
128 if (ASMAtomicCmpXchgExS32(&pOnce->cEventRefs, cEventRefs + 1, cEventRefs, &cEventRefs))
129 break;
130 ASMNopPause();
131 }
132 if (cEventRefs <= 0)
133 break;
134
135 ASMAtomicReadHandle(&pOnce->hEventMulti, phEvtM);
136 AssertReturn(*phEvtM != NIL_RTSEMEVENTMULTI, VERR_INTERNAL_ERROR_2);
137 }
138
139 /*
140 * We've got a sempahore, do the actual waiting.
141 */
142 do
143 RTSemEventMultiWaitNoResume(*phEvtM, RT_INDEFINITE_WAIT);
144 while (ASMAtomicReadS32(&pOnce->iState) == RTONCESTATE_BUSY_HAVE_SEM);
145 break;
146 }
147
148 case RTONCESTATE_DONE_CREATING_SEM:
149 case RTONCESTATE_DONE:
150 return VINF_SUCCESS;
151
152 default:
153 AssertMsgFailedReturn(("%d\n", iState), VERR_INTERNAL_ERROR_3);
154 }
155 }
156}
157
158
159RTDECL(int) RTOnce(PRTONCE pOnce, PFNRTONCE pfnOnce, void *pvUser1, void *pvUser2)
160{
161 /*
162 * Validate input (strict builds only).
163 */
164 AssertPtr(pOnce);
165 AssertPtr(pfnOnce);
166
167 /*
168 * Deal with the 'initialized' case first
169 */
170 int32_t iState = ASMAtomicUoReadS32(&pOnce->iState);
171 if (RT_LIKELY( iState == RTONCESTATE_DONE
172 || iState == RTONCESTATE_DONE_CREATING_SEM
173 || iState == RTONCESTATE_DONE_HAVE_SEM
174 ))
175 return ASMAtomicUoReadS32(&pOnce->rc);
176
177 AssertReturn( iState == RTONCESTATE_UNINITIALIZED
178 || iState == RTONCESTATE_BUSY_NO_SEM
179 || iState == RTONCESTATE_BUSY_SPIN
180 || iState == RTONCESTATE_BUSY_CREATING_SEM
181 || iState == RTONCESTATE_BUSY_HAVE_SEM
182 , VERR_INTERNAL_ERROR);
183
184 /*
185 * Do we initialize it?
186 */
187 int32_t rcOnce;
188 if ( iState == RTONCESTATE_UNINITIALIZED
189 && ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_BUSY_NO_SEM, RTONCESTATE_UNINITIALIZED))
190 {
191 /*
192 * Yes, so do the execute once stuff.
193 */
194 rcOnce = pfnOnce(pvUser1, pvUser2);
195 ASMAtomicWriteS32(&pOnce->rc, rcOnce);
196
197 /*
198 * If there is a sempahore to signal, we're in for some extra work here.
199 */
200 if ( !ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_DONE, RTONCESTATE_BUSY_NO_SEM)
201 && !ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_DONE, RTONCESTATE_BUSY_SPIN)
202 && !ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_DONE_CREATING_SEM, RTONCESTATE_BUSY_CREATING_SEM)
203 )
204 {
205 /* Grab the sempahore by switching to 'DONE_HAVE_SEM' before reaching 'DONE'. */
206 AssertReturn(ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_DONE_HAVE_SEM, RTONCESTATE_BUSY_HAVE_SEM),
207 VERR_INTERNAL_ERROR_2);
208
209 int32_t cRefs = ASMAtomicIncS32(&pOnce->cEventRefs);
210 Assert(cRefs > 1);
211
212 RTSEMEVENTMULTI hEvtM;
213 ASMAtomicReadHandle(&pOnce->hEventMulti, &hEvtM);
214 Assert(hEvtM != NIL_RTSEMEVENTMULTI);
215
216 ASMAtomicWriteS32(&pOnce->iState, RTONCESTATE_DONE);
217
218 /* Signal it and return. */
219 RTSemEventMultiSignal(hEvtM);
220 }
221 }
222 else
223 {
224 /*
225 * Wait for the first thread to complete. Delegate this to a helper
226 * function to simplify cleanup and keep things a bit shorter.
227 */
228 RTSEMEVENTMULTI hEvtM = NIL_RTSEMEVENTMULTI;
229 rcOnce = rtOnceOtherThread(pOnce, &hEvtM);
230 if (hEvtM != NIL_RTSEMEVENTMULTI)
231 {
232 if (ASMAtomicDecS32(&pOnce->cEventRefs) == 0)
233 {
234 bool fRc;
235 ASMAtomicCmpXchgHandle(&pOnce->hEventMulti, NIL_RTSEMEVENTMULTI, hEvtM, fRc); Assert(fRc);
236 fRc = ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_DONE, RTONCESTATE_DONE_HAVE_SEM); Assert(fRc);
237 RTSemEventMultiDestroy(hEvtM);
238 }
239 }
240 if (RT_SUCCESS(rcOnce))
241 rcOnce = ASMAtomicUoReadS32(&pOnce->rc);
242 }
243
244 return rcOnce;
245}
246RT_EXPORT_SYMBOL(RTOnce);
247
248
249RTDECL(void) RTOnceReset(PRTONCE pOnce)
250{
251 /* Cannot be done while busy! */
252 AssertPtr(pOnce);
253 Assert(pOnce->hEventMulti == NIL_RTSEMEVENTMULTI);
254 int32_t iState = ASMAtomicUoReadS32(&pOnce->iState);
255 AssertMsg( iState == RTONCESTATE_DONE
256 && iState == RTONCESTATE_UNINITIALIZED,
257 ("%d\n", iState));
258
259 /* Do the same as RTONCE_INITIALIZER does. */
260 ASMAtomicWriteS32(&pOnce->rc, VERR_INTERNAL_ERROR);
261 ASMAtomicWriteS32(&pOnce->iState, RTONCESTATE_UNINITIALIZED);
262}
263RT_EXPORT_SYMBOL(RTOnceReset);
264
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette