VirtualBox

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

Last change on this file since 92154 was 91503, checked in by vboxsync, 3 years ago

Main: bugref:1909: Added missed translation marks, removed redundant ones. Expanded one macro to make the lupdate get string correctly. Removed GuestBase::setErrorExternal and changed calls from it to setErrorBoth to handle translation correctly.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use