VirtualBox

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

Last change on this file since 93412 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.3 KB
Line 
1/* $Id: AutoCaller.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * VirtualBox object state implementation
4 */
5
6/*
7 * Copyright (C) 2006-2022 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#include "VBoxNls.h"
26
27
28DECLARE_TRANSLATION_CONTEXT(AutoCallerCtx);
29
30////////////////////////////////////////////////////////////////////////////////
31//
32// ObjectState methods
33//
34////////////////////////////////////////////////////////////////////////////////
35
36
37ObjectState::ObjectState() : mStateLock(LOCKCLASS_OBJECTSTATE)
38{
39 AssertFailed();
40}
41
42ObjectState::ObjectState(VirtualBoxBase *aObj) :
43 mObj(aObj), mStateLock(LOCKCLASS_OBJECTSTATE)
44{
45 Assert(mObj);
46 mState = NotReady;
47 mStateChangeThread = NIL_RTTHREAD;
48 mCallers = 0;
49 mFailedRC = S_OK;
50 mpFailedEI = NULL;
51 mZeroCallersSem = NIL_RTSEMEVENT;
52 mInitUninitSem = NIL_RTSEMEVENTMULTI;
53 mInitUninitWaiters = 0;
54}
55
56ObjectState::~ObjectState()
57{
58 Assert(mInitUninitWaiters == 0);
59 Assert(mInitUninitSem == NIL_RTSEMEVENTMULTI);
60 if (mZeroCallersSem != NIL_RTSEMEVENT)
61 RTSemEventDestroy(mZeroCallersSem);
62 mCallers = 0;
63 mStateChangeThread = NIL_RTTHREAD;
64 mState = NotReady;
65 mFailedRC = S_OK;
66 if (mpFailedEI)
67 {
68 delete mpFailedEI;
69 mpFailedEI = NULL;
70 }
71 mObj = NULL;
72}
73
74ObjectState::State ObjectState::getState()
75{
76 AutoReadLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
77 return mState;
78}
79
80/**
81 * Increments the number of calls to this object by one.
82 *
83 * After this method succeeds, it is guaranteed that the object will remain
84 * in the Ready (or in the Limited) state at least until #releaseCaller() is
85 * called.
86 *
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.
94 *
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.
101 *
102 * This method succeeds (and increments the number of callers) only if the
103 * current object's state is Ready. Otherwise, it will return E_ACCESSDENIED
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
108 * succeed if the object's state is Limited (or Ready, of course).
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>
116 *
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.
122 *
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.
125 *
126 * @param aLimited |true| to add a limited caller.
127 *
128 * @return S_OK on success or E_ACCESSDENIED on failure.
129 *
130 * @sa #releaseCaller()
131 */
132HRESULT ObjectState::addCaller(bool aLimited /* = false */)
133{
134 AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
135
136 HRESULT rc = E_ACCESSDENIED;
137
138 if (mState == Ready || (aLimited && mState == Limited))
139 {
140 /* if Ready or allows Limited, increase the number of callers */
141 ++mCallers;
142 rc = S_OK;
143 }
144 else
145 if (mState == InInit || mState == InUninit)
146 {
147 if (mStateChangeThread == RTThreadSelf())
148 {
149 /* Called from the same thread that is doing AutoInitSpan or
150 * AutoUninitSpan, just succeed */
151 rc = S_OK;
152 }
153 else if (mState == InInit)
154 {
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).
159 *
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.
164 */
165 ++mCallers;
166
167 /* lazy semaphore creation */
168 if (mInitUninitSem == NIL_RTSEMEVENTMULTI)
169 {
170 RTSemEventMultiCreate(&mInitUninitSem);
171 Assert(mInitUninitWaiters == 0);
172 }
173
174 ++mInitUninitWaiters;
175
176 LogFlowThisFunc(("Waiting for AutoInitSpan/AutoReinitSpan to finish...\n"));
177
178 stateLock.release();
179 RTSemEventMultiWait(mInitUninitSem, RT_INDEFINITE_WAIT);
180 stateLock.acquire();
181
182 if (--mInitUninitWaiters == 0)
183 {
184 /* destroy the semaphore since no more necessary */
185 RTSemEventMultiDestroy(mInitUninitSem);
186 mInitUninitSem = NIL_RTSEMEVENTMULTI;
187 }
188
189 if (mState == Ready || (aLimited && mState == Limited))
190 rc = S_OK;
191 else
192 {
193 Assert(mCallers != 0);
194 --mCallers;
195 if (mCallers == 0 && mState == InUninit)
196 {
197 /* inform AutoUninitSpan ctor there are no more callers */
198 RTSemEventSignal(mZeroCallersSem);
199 }
200 }
201 }
202 }
203
204 if (FAILED(rc))
205 {
206 if (mState == Limited)
207 rc = mObj->setError(rc, AutoCallerCtx::tr("The object functionality is limited"));
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 }
215 else
216 rc = mObj->setError(rc, AutoCallerCtx::tr("The object is not ready"));
217 }
218
219 return rc;
220}
221
222/**
223 * Decreases the number of calls to this object by one.
224 *
225 * Must be called after every #addCaller() when protecting the object
226 * from uninitialization is no more necessary.
227 */
228void ObjectState::releaseCaller()
229{
230 AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
231
232 if (mState == Ready || mState == Limited)
233 {
234 /* if Ready or Limited, decrease the number of callers */
235 AssertMsgReturn(mCallers != 0, ("mCallers is ZERO!"), (void) 0);
236 --mCallers;
237
238 return;
239 }
240
241 if (mState == InInit || mState == InUninit)
242 {
243 if (mStateChangeThread == RTThreadSelf())
244 {
245 /* Called from the same thread that is doing AutoInitSpan or
246 * AutoUninitSpan: just succeed */
247 return;
248 }
249
250 if (mState == InUninit)
251 {
252 /* the caller is being released after AutoUninitSpan has begun */
253 AssertMsgReturn(mCallers != 0, ("mCallers is ZERO!"), (void) 0);
254 --mCallers;
255
256 if (mCallers == 0)
257 /* inform the Auto*UninitSpan ctor there are no more callers */
258 RTSemEventSignal(mZeroCallersSem);
259
260 return;
261 }
262 }
263
264 AssertMsgFailed(("mState = %d!", mState));
265}
266
267bool ObjectState::autoInitSpanConstructor(ObjectState::State aExpectedState)
268{
269 AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
270
271 mFailedRC = S_OK;
272 if (mpFailedEI)
273 {
274 delete mpFailedEI;
275 mpFailedEI = NULL;
276 }
277
278 if (mState == aExpectedState)
279 {
280 setState(InInit);
281 return true;
282 }
283 else
284 return false;
285}
286
287void ObjectState::autoInitSpanDestructor(State aNewState, HRESULT aFailedRC, com::ErrorInfo *apFailedEI)
288{
289 AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
290
291 Assert(mState == InInit);
292
293 if (mCallers > 0 && mInitUninitWaiters > 0)
294 {
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);
298 }
299
300 if (aNewState == InitFailed || aNewState == Limited)
301 {
302 mFailedRC = aFailedRC;
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. */
307 mpFailedEI = apFailedEI;
308 }
309 else
310 {
311 Assert(SUCCEEDED(aFailedRC));
312 Assert(apFailedEI == NULL);
313 Assert(mpFailedEI == NULL);
314 }
315
316 setState(aNewState);
317}
318
319ObjectState::State ObjectState::autoUninitSpanConstructor(bool fTry)
320{
321 AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
322
323 Assert(mState != InInit);
324
325 if (mState == NotReady)
326 {
327 /* do nothing if already uninitialized */
328 return mState;
329 }
330 else if (mState == InUninit)
331 {
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). */
335
336 if (fTry)
337 return Ready;
338
339 /* lazy semaphore creation */
340 if (mInitUninitSem == NIL_RTSEMEVENTMULTI)
341 {
342 RTSemEventMultiCreate(&mInitUninitSem);
343 Assert(mInitUninitWaiters == 0);
344 }
345 ++mInitUninitWaiters;
346
347 LogFlowFunc(("{%p}: Waiting for AutoUninitSpan to finish...\n", mObj));
348
349 stateLock.release();
350 RTSemEventMultiWait(mInitUninitSem, RT_INDEFINITE_WAIT);
351 stateLock.acquire();
352
353 if (--mInitUninitWaiters == 0)
354 {
355 /* destroy the semaphore since no more necessary */
356 RTSemEventMultiDestroy(mInitUninitSem);
357 mInitUninitSem = NIL_RTSEMEVENTMULTI;
358 }
359
360 /* the other thread set it to NotReady */
361 return mState;
362 }
363
364 /* go to InUninit to prevent from adding new callers */
365 setState(InUninit);
366
367 /* wait for already existing callers to drop to zero */
368 if (mCallers > 0)
369 {
370 if (fTry)
371 return Ready;
372
373 /* lazy creation */
374 Assert(mZeroCallersSem == NIL_RTSEMEVENT);
375 RTSemEventCreate(&mZeroCallersSem);
376
377 /* wait until remaining callers release the object */
378 LogFlowFunc(("{%p}: Waiting for callers (%d) to drop to zero...\n",
379 mObj, mCallers));
380
381 stateLock.release();
382 RTSemEventWait(mZeroCallersSem, RT_INDEFINITE_WAIT);
383 }
384 return mState;
385}
386
387void ObjectState::autoUninitSpanDestructor()
388{
389 AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
390
391 Assert(mState == InUninit);
392
393 setState(NotReady);
394}
395
396
397void ObjectState::setState(ObjectState::State aState)
398{
399 Assert(mState != aState);
400 mState = aState;
401 mStateChangeThread = RTThreadSelf();
402}
403
404
405////////////////////////////////////////////////////////////////////////////////
406//
407// AutoInitSpan methods
408//
409////////////////////////////////////////////////////////////////////////////////
410
411/**
412 * Creates a smart initialization span object that places the object to
413 * InInit state.
414 *
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.
420 */
421AutoInitSpan::AutoInitSpan(VirtualBoxBase *aObj,
422 Result aResult /* = Failed */)
423 : mObj(aObj),
424 mResult(aResult),
425 mOk(false),
426 mFailedRC(S_OK),
427 mpFailedEI(NULL)
428{
429 Assert(mObj);
430 mOk = mObj->getObjectState().autoInitSpanConstructor(ObjectState::NotReady);
431 AssertReturnVoid(mOk);
432}
433
434/**
435 * Places the managed VirtualBoxBase object to Ready/Limited state if the
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.
440 */
441AutoInitSpan::~AutoInitSpan()
442{
443 /* if the state was other than NotReady, do nothing */
444 if (!mOk)
445 {
446 Assert(SUCCEEDED(mFailedRC));
447 Assert(mpFailedEI == NULL);
448 return;
449 }
450
451 ObjectState::State newState;
452 if (mResult == Succeeded)
453 newState = ObjectState::Ready;
454 else if (mResult == Limited)
455 newState = ObjectState::Limited;
456 else
457 newState = ObjectState::InitFailed;
458 mObj->getObjectState().autoInitSpanDestructor(newState, mFailedRC, mpFailedEI);
459 mFailedRC = S_OK;
460 mpFailedEI = NULL; /* now owned by ObjectState instance */
461 if (newState == ObjectState::InitFailed)
462 {
463 /* call uninit() to let the object uninit itself after failed init() */
464 mObj->uninit();
465 }
466}
467
468// AutoReinitSpan methods
469////////////////////////////////////////////////////////////////////////////////
470
471/**
472 * Creates a smart re-initialization span object and places the object to
473 * InInit state.
474 *
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.
479 */
480AutoReinitSpan::AutoReinitSpan(VirtualBoxBase *aObj)
481 : mObj(aObj),
482 mSucceeded(false),
483 mOk(false)
484{
485 Assert(mObj);
486 mOk = mObj->getObjectState().autoInitSpanConstructor(ObjectState::Limited);
487 AssertReturnVoid(mOk);
488}
489
490/**
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.
496 */
497AutoReinitSpan::~AutoReinitSpan()
498{
499 /* if the state was other than Limited, do nothing */
500 if (!mOk)
501 return;
502
503 ObjectState::State newState;
504 if (mSucceeded)
505 newState = ObjectState::Ready;
506 else
507 newState = ObjectState::Limited;
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. */
514}
515
516// AutoUninitSpan methods
517////////////////////////////////////////////////////////////////////////////////
518
519/**
520 * Creates a smart uninitialization span object and places this object to
521 * InUninit state.
522 *
523 * Please see the AutoInitSpan class description for more info.
524 *
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.
530 * @param fTry @c true if the wait for other callers should be skipped,
531 * requiring checking if the uninit span is actually operational.
532 */
533AutoUninitSpan::AutoUninitSpan(VirtualBoxBase *aObj, bool fTry /* = false */)
534 : mObj(aObj),
535 mInitFailed(false),
536 mUninitDone(false),
537 mUninitFailed(false)
538{
539 Assert(mObj);
540 ObjectState::State state;
541 state = mObj->getObjectState().autoUninitSpanConstructor(fTry);
542 if (state == ObjectState::InitFailed)
543 mInitFailed = true;
544 else if (state == ObjectState::NotReady)
545 mUninitDone = true;
546 else if (state == ObjectState::Ready)
547 mUninitFailed = true;
548}
549
550/**
551 * Places the managed VirtualBoxBase object to the NotReady state.
552 */
553AutoUninitSpan::~AutoUninitSpan()
554{
555 /* do nothing if already uninitialized */
556 if (mUninitDone || mUninitFailed)
557 return;
558
559 mObj->getObjectState().autoUninitSpanDestructor();
560}
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 */
570 if (mUninitDone || mUninitFailed)
571 return;
572
573 mObj->getObjectState().autoUninitSpanDestructor();
574 mUninitDone = true;
575}
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