VirtualBox

source: vbox/trunk/src/VBox/Main/src-all/AutoCaller.cpp@ 76690

Last change on this file since 76690 was 76592, checked in by vboxsync, 6 years ago

Main: Don't use Logging.h.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.1 KB
Line 
1/* $Id: AutoCaller.cpp 76592 2019-01-01 20:13:07Z vboxsync $ */
2/** @file
3 * VirtualBox object state implementation
4 */
5
6/*
7 * Copyright (C) 2006-2019 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
18#define LOG_GROUP LOG_GROUP_MAIN
19#include <iprt/semaphore.h>
20
21#include "VirtualBoxBase.h"
22#include "AutoCaller.h"
23#include "LoggingNew.h"
24
25
26////////////////////////////////////////////////////////////////////////////////
27//
28// ObjectState methods
29//
30////////////////////////////////////////////////////////////////////////////////
31
32
33ObjectState::ObjectState() : mStateLock(LOCKCLASS_OBJECTSTATE)
34{
35 AssertFailed();
36}
37
38ObjectState::ObjectState(VirtualBoxBase *aObj) :
39 mObj(aObj), mStateLock(LOCKCLASS_OBJECTSTATE)
40{
41 Assert(mObj);
42 mState = NotReady;
43 mStateChangeThread = NIL_RTTHREAD;
44 mCallers = 0;
45 mFailedRC = S_OK;
46 mpFailedEI = NULL;
47 mZeroCallersSem = NIL_RTSEMEVENT;
48 mInitUninitSem = NIL_RTSEMEVENTMULTI;
49 mInitUninitWaiters = 0;
50}
51
52ObjectState::~ObjectState()
53{
54 Assert(mInitUninitWaiters == 0);
55 Assert(mInitUninitSem == NIL_RTSEMEVENTMULTI);
56 if (mZeroCallersSem != NIL_RTSEMEVENT)
57 RTSemEventDestroy(mZeroCallersSem);
58 mCallers = 0;
59 mStateChangeThread = NIL_RTTHREAD;
60 mState = NotReady;
61 mFailedRC = S_OK;
62 if (mpFailedEI)
63 {
64 delete mpFailedEI;
65 mpFailedEI = NULL;
66 }
67 mObj = NULL;
68}
69
70ObjectState::State ObjectState::getState()
71{
72 AutoReadLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
73 return mState;
74}
75
76/**
77 * Increments the number of calls to this object by one.
78 *
79 * After this method succeeds, it is guaranteed that the object will remain
80 * in the Ready (or in the Limited) state at least until #releaseCaller() is
81 * called.
82 *
83 * This method is intended to mark the beginning of sections of code within
84 * methods of COM objects that depend on the readiness (Ready) state. The
85 * Ready state is a primary "ready to serve" state. Usually all code that
86 * works with component's data depends on it. On practice, this means that
87 * almost every public method, setter or getter of the object should add
88 * itself as an object's caller at the very beginning, to protect from an
89 * unexpected uninitialization that may happen on a different thread.
90 *
91 * Besides the Ready state denoting that the object is fully functional,
92 * there is a special Limited state. The Limited state means that the object
93 * is still functional, but its functionality is limited to some degree, so
94 * not all operations are possible. The @a aLimited argument to this method
95 * determines whether the caller represents this limited functionality or
96 * not.
97 *
98 * This method succeeds (and increments the number of callers) only if the
99 * current object's state is Ready. Otherwise, it will return E_ACCESSDENIED
100 * to indicate that the object is not operational. There are two exceptions
101 * from this rule:
102 * <ol>
103 * <li>If the @a aLimited argument is |true|, then this method will also
104 * succeed if the object's state is Limited (or Ready, of course).
105 * </li>
106 * <li>If this method is called from the same thread that placed
107 * the object to InInit or InUninit state (i.e. either from within the
108 * AutoInitSpan or AutoUninitSpan scope), it will succeed as well (but
109 * will not increase the number of callers).
110 * </li>
111 * </ol>
112 *
113 * Normally, calling addCaller() never blocks. However, if this method is
114 * called by a thread created from within the AutoInitSpan scope and this
115 * scope is still active (i.e. the object state is InInit), it will block
116 * until the AutoInitSpan destructor signals that it has finished
117 * initialization.
118 *
119 * When this method returns a failure, the caller must not use the object
120 * and should return the failed result code to its own caller.
121 *
122 * @param aLimited |true| to add a limited caller.
123 *
124 * @return S_OK on success or E_ACCESSDENIED on failure.
125 *
126 * @sa #releaseCaller()
127 */
128HRESULT ObjectState::addCaller(bool aLimited /* = false */)
129{
130 AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
131
132 HRESULT rc = E_ACCESSDENIED;
133
134 if (mState == Ready || (aLimited && mState == Limited))
135 {
136 /* if Ready or allows Limited, increase the number of callers */
137 ++mCallers;
138 rc = S_OK;
139 }
140 else
141 if (mState == InInit || mState == InUninit)
142 {
143 if (mStateChangeThread == RTThreadSelf())
144 {
145 /* Called from the same thread that is doing AutoInitSpan or
146 * AutoUninitSpan, just succeed */
147 rc = S_OK;
148 }
149 else if (mState == InInit)
150 {
151 /* addCaller() is called by a "child" thread while the "parent"
152 * thread is still doing AutoInitSpan/AutoReinitSpan, so wait for
153 * the state to become either Ready/Limited or InitFailed (in
154 * case of init failure).
155 *
156 * Note that we increase the number of callers anyway -- to
157 * prevent AutoUninitSpan from early completion if we are
158 * still not scheduled to pick up the posted semaphore when
159 * uninit() is called.
160 */
161 ++mCallers;
162
163 /* lazy semaphore creation */
164 if (mInitUninitSem == NIL_RTSEMEVENTMULTI)
165 {
166 RTSemEventMultiCreate(&mInitUninitSem);
167 Assert(mInitUninitWaiters == 0);
168 }
169
170 ++mInitUninitWaiters;
171
172 LogFlowThisFunc(("Waiting for AutoInitSpan/AutoReinitSpan to finish...\n"));
173
174 stateLock.release();
175 RTSemEventMultiWait(mInitUninitSem, RT_INDEFINITE_WAIT);
176 stateLock.acquire();
177
178 if (--mInitUninitWaiters == 0)
179 {
180 /* destroy the semaphore since no more necessary */
181 RTSemEventMultiDestroy(mInitUninitSem);
182 mInitUninitSem = NIL_RTSEMEVENTMULTI;
183 }
184
185 if (mState == Ready || (aLimited && mState == Limited))
186 rc = S_OK;
187 else
188 {
189 Assert(mCallers != 0);
190 --mCallers;
191 if (mCallers == 0 && mState == InUninit)
192 {
193 /* inform AutoUninitSpan ctor there are no more callers */
194 RTSemEventSignal(mZeroCallersSem);
195 }
196 }
197 }
198 }
199
200 if (FAILED(rc))
201 {
202 if (mState == Limited)
203 rc = mObj->setError(rc, "The object functionality is limited");
204 else if (FAILED(mFailedRC) && mFailedRC != E_ACCESSDENIED)
205 {
206 /* replay recorded error information */
207 if (mpFailedEI)
208 ErrorInfoKeeper eik(*mpFailedEI);
209 rc = mFailedRC;
210 }
211 else
212 rc = mObj->setError(rc, "The object is not ready");
213 }
214
215 return rc;
216}
217
218/**
219 * Decreases the number of calls to this object by one.
220 *
221 * Must be called after every #addCaller() when protecting the object
222 * from uninitialization is no more necessary.
223 */
224void ObjectState::releaseCaller()
225{
226 AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
227
228 if (mState == Ready || mState == Limited)
229 {
230 /* if Ready or Limited, decrease the number of callers */
231 AssertMsgReturn(mCallers != 0, ("mCallers is ZERO!"), (void) 0);
232 --mCallers;
233
234 return;
235 }
236
237 if (mState == InInit || mState == InUninit)
238 {
239 if (mStateChangeThread == RTThreadSelf())
240 {
241 /* Called from the same thread that is doing AutoInitSpan or
242 * AutoUninitSpan: just succeed */
243 return;
244 }
245
246 if (mState == InUninit)
247 {
248 /* the caller is being released after AutoUninitSpan has begun */
249 AssertMsgReturn(mCallers != 0, ("mCallers is ZERO!"), (void) 0);
250 --mCallers;
251
252 if (mCallers == 0)
253 /* inform the Auto*UninitSpan ctor there are no more callers */
254 RTSemEventSignal(mZeroCallersSem);
255
256 return;
257 }
258 }
259
260 AssertMsgFailed(("mState = %d!", mState));
261}
262
263bool ObjectState::autoInitSpanConstructor(ObjectState::State aExpectedState)
264{
265 AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
266
267 mFailedRC = S_OK;
268 if (mpFailedEI)
269 {
270 delete mpFailedEI;
271 mpFailedEI = NULL;
272 }
273
274 if (mState == aExpectedState)
275 {
276 setState(InInit);
277 return true;
278 }
279 else
280 return false;
281}
282
283void ObjectState::autoInitSpanDestructor(State aNewState, HRESULT aFailedRC, com::ErrorInfo *apFailedEI)
284{
285 AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
286
287 Assert(mState == InInit);
288
289 if (mCallers > 0 && mInitUninitWaiters > 0)
290 {
291 /* We have some pending addCaller() calls on other threads (created
292 * during InInit), signal that InInit is finished and they may go on. */
293 RTSemEventMultiSignal(mInitUninitSem);
294 }
295
296 if (aNewState == InitFailed)
297 {
298 mFailedRC = aFailedRC;
299 /* apFailedEI may be NULL, when there is no explicit setFailed() call,
300 * which also implies that aFailedRC is S_OK. This case is used by
301 * objects (the majority) which don't want delayed error signalling. */
302 mpFailedEI = apFailedEI;
303 }
304 else
305 {
306 Assert(SUCCEEDED(aFailedRC));
307 Assert(apFailedEI == NULL);
308 Assert(mpFailedEI == NULL);
309 }
310
311 setState(aNewState);
312}
313
314ObjectState::State ObjectState::autoUninitSpanConstructor(bool fTry)
315{
316 AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
317
318 Assert(mState != InInit);
319
320 if (mState == NotReady)
321 {
322 /* do nothing if already uninitialized */
323 return mState;
324 }
325 else if (mState == InUninit)
326 {
327 /* Another thread has already started uninitialization, wait for its
328 * completion. This is necessary to make sure that when this method
329 * returns, the object state is well-defined (NotReady). */
330
331 if (fTry)
332 return Ready;
333
334 /* lazy semaphore creation */
335 if (mInitUninitSem == NIL_RTSEMEVENTMULTI)
336 {
337 RTSemEventMultiCreate(&mInitUninitSem);
338 Assert(mInitUninitWaiters == 0);
339 }
340 ++mInitUninitWaiters;
341
342 LogFlowFunc(("{%p}: Waiting for AutoUninitSpan to finish...\n", mObj));
343
344 stateLock.release();
345 RTSemEventMultiWait(mInitUninitSem, RT_INDEFINITE_WAIT);
346 stateLock.acquire();
347
348 if (--mInitUninitWaiters == 0)
349 {
350 /* destroy the semaphore since no more necessary */
351 RTSemEventMultiDestroy(mInitUninitSem);
352 mInitUninitSem = NIL_RTSEMEVENTMULTI;
353 }
354
355 /* the other thread set it to NotReady */
356 return mState;
357 }
358
359 /* go to InUninit to prevent from adding new callers */
360 setState(InUninit);
361
362 /* wait for already existing callers to drop to zero */
363 if (mCallers > 0)
364 {
365 if (fTry)
366 return Ready;
367
368 /* lazy creation */
369 Assert(mZeroCallersSem == NIL_RTSEMEVENT);
370 RTSemEventCreate(&mZeroCallersSem);
371
372 /* wait until remaining callers release the object */
373 LogFlowFunc(("{%p}: Waiting for callers (%d) to drop to zero...\n",
374 mObj, mCallers));
375
376 stateLock.release();
377 RTSemEventWait(mZeroCallersSem, RT_INDEFINITE_WAIT);
378 }
379 return mState;
380}
381
382void ObjectState::autoUninitSpanDestructor()
383{
384 AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
385
386 Assert(mState == InUninit);
387
388 setState(NotReady);
389}
390
391
392void ObjectState::setState(ObjectState::State aState)
393{
394 Assert(mState != aState);
395 mState = aState;
396 mStateChangeThread = RTThreadSelf();
397}
398
399
400////////////////////////////////////////////////////////////////////////////////
401//
402// AutoInitSpan methods
403//
404////////////////////////////////////////////////////////////////////////////////
405
406/**
407 * Creates a smart initialization span object that places the object to
408 * InInit state.
409 *
410 * Please see the AutoInitSpan class description for more info.
411 *
412 * @param aObj |this| pointer of the managed VirtualBoxBase object whose
413 * init() method is being called.
414 * @param aResult Default initialization result.
415 */
416AutoInitSpan::AutoInitSpan(VirtualBoxBase *aObj,
417 Result aResult /* = Failed */)
418 : mObj(aObj),
419 mResult(aResult),
420 mOk(false),
421 mFailedRC(S_OK),
422 mpFailedEI(NULL)
423{
424 Assert(mObj);
425 mOk = mObj->getObjectState().autoInitSpanConstructor(ObjectState::NotReady);
426 AssertReturnVoid(mOk);
427}
428
429/**
430 * Places the managed VirtualBoxBase object to Ready/Limited state if the
431 * initialization succeeded or partly succeeded, or places it to InitFailed
432 * state and calls the object's uninit() method.
433 *
434 * Please see the AutoInitSpan class description for more info.
435 */
436AutoInitSpan::~AutoInitSpan()
437{
438 /* if the state was other than NotReady, do nothing */
439 if (!mOk)
440 {
441 Assert(SUCCEEDED(mFailedRC));
442 Assert(mpFailedEI == NULL);
443 return;
444 }
445
446 ObjectState::State newState;
447 if (mResult == Succeeded)
448 newState = ObjectState::Ready;
449 else if (mResult == Limited)
450 newState = ObjectState::Limited;
451 else
452 newState = ObjectState::InitFailed;
453 mObj->getObjectState().autoInitSpanDestructor(newState, mFailedRC, mpFailedEI);
454 mFailedRC = S_OK;
455 mpFailedEI = NULL; /* now owned by ObjectState instance */
456 if (newState == ObjectState::InitFailed)
457 {
458 /* call uninit() to let the object uninit itself after failed init() */
459 mObj->uninit();
460 }
461}
462
463// AutoReinitSpan methods
464////////////////////////////////////////////////////////////////////////////////
465
466/**
467 * Creates a smart re-initialization span object and places the object to
468 * InInit state.
469 *
470 * Please see the AutoInitSpan class description for more info.
471 *
472 * @param aObj |this| pointer of the managed VirtualBoxBase object whose
473 * re-initialization method is being called.
474 */
475AutoReinitSpan::AutoReinitSpan(VirtualBoxBase *aObj)
476 : mObj(aObj),
477 mSucceeded(false),
478 mOk(false)
479{
480 Assert(mObj);
481 mOk = mObj->getObjectState().autoInitSpanConstructor(ObjectState::Limited);
482 AssertReturnVoid(mOk);
483}
484
485/**
486 * Places the managed VirtualBoxBase object to Ready state if the
487 * re-initialization succeeded (i.e. #setSucceeded() has been called) or back to
488 * Limited state otherwise.
489 *
490 * Please see the AutoInitSpan class description for more info.
491 */
492AutoReinitSpan::~AutoReinitSpan()
493{
494 /* if the state was other than Limited, do nothing */
495 if (!mOk)
496 return;
497
498 ObjectState::State newState;
499 if (mSucceeded)
500 newState = ObjectState::Ready;
501 else
502 newState = ObjectState::Limited;
503 mObj->getObjectState().autoInitSpanDestructor(newState, S_OK, NULL);
504 /* If later AutoReinitSpan can truly fail (today there is no way) then
505 * in this place there needs to be an mObj->uninit() call just like in
506 * the AutoInitSpan destructor. In that case it might make sense to
507 * let AutoReinitSpan inherit from AutoInitSpan, as the code can be
508 * made (almost) identical. */
509}
510
511// AutoUninitSpan methods
512////////////////////////////////////////////////////////////////////////////////
513
514/**
515 * Creates a smart uninitialization span object and places this object to
516 * InUninit state.
517 *
518 * Please see the AutoInitSpan class description for more info.
519 *
520 * @note This method blocks the current thread execution until the number of
521 * callers of the managed VirtualBoxBase object drops to zero!
522 *
523 * @param aObj |this| pointer of the VirtualBoxBase object whose uninit()
524 * method is being called.
525 * @param fTry @c true if the wait for other callers should be skipped,
526 * requiring checking if the uninit span is actually operational.
527 */
528AutoUninitSpan::AutoUninitSpan(VirtualBoxBase *aObj, bool fTry /* = false */)
529 : mObj(aObj),
530 mInitFailed(false),
531 mUninitDone(false),
532 mUninitFailed(false)
533{
534 Assert(mObj);
535 ObjectState::State state;
536 state = mObj->getObjectState().autoUninitSpanConstructor(fTry);
537 if (state == ObjectState::InitFailed)
538 mInitFailed = true;
539 else if (state == ObjectState::NotReady)
540 mUninitDone = true;
541 else if (state == ObjectState::Ready)
542 mUninitFailed = true;
543}
544
545/**
546 * Places the managed VirtualBoxBase object to the NotReady state.
547 */
548AutoUninitSpan::~AutoUninitSpan()
549{
550 /* do nothing if already uninitialized */
551 if (mUninitDone || mUninitFailed)
552 return;
553
554 mObj->getObjectState().autoUninitSpanDestructor();
555}
556
557/**
558 * Marks the uninitializion as succeeded.
559 *
560 * Same as the destructor, and makes the destructor do nothing.
561 */
562void AutoUninitSpan::setSucceeded()
563{
564 /* do nothing if already uninitialized */
565 if (mUninitDone || mUninitFailed)
566 return;
567
568 mObj->getObjectState().autoUninitSpanDestructor();
569 mUninitDone = true;
570}
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