VirtualBox

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

Last change on this file since 53390 was 52095, checked in by vboxsync, 10 years ago

Main/Medium+AutoCaller+others: fix medium uninit deadlock caused by lock order violations, sometimes taking the caller before the media tree lock, sometimes after, plus a few other small fixes

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