VirtualBox

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

Last change on this file since 67954 was 59996, checked in by vboxsync, 9 years ago

Main/VirtualBox: postpone the error reporting from VirtualBox object creation to the method calls of the object. COM loses the error (replaces it by REGDB_E_CLASSNOTREG), making troubleshooting very difficult. XPCOM wouldn't need this, but it is applied everywhere for maximum consistency. Many changes elsewhere to propagate the information correctly, and also fixes many outdated comments.

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