VirtualBox

source: vbox/trunk/src/VBox/Main/include/AutoCaller.h

Last change on this file was 98263, checked in by vboxsync, 16 months ago

Main: rc() -> hrc()/vrc().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.9 KB
RevLine 
[55401]1/* $Id: AutoCaller.h 98263 2023-01-24 01:52:49Z vboxsync $ */
[1]2/** @file
3 *
[51903]4 * VirtualBox object caller handling definitions
[1]5 */
6
7/*
[98103]8 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[1]9 *
[96407]10 * This file is part of VirtualBox base platform packages, as
11 * available from https://www.virtualbox.org.
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation, in version 3 of the
16 * License.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses>.
25 *
26 * SPDX-License-Identifier: GPL-3.0-only
[1]27 */
28
[76562]29#ifndef MAIN_INCLUDED_AutoCaller_h
30#define MAIN_INCLUDED_AutoCaller_h
[76487]31#ifndef RT_WITHOUT_PRAGMA_ONCE
32# pragma once
33#endif
[1]34
[51903]35#include "ObjectState.h"
36
37#include "VBox/com/AutoLock.h"
38
39// Forward declaration needed, but nothing more.
40class VirtualBoxBase;
41
42
[1]43////////////////////////////////////////////////////////////////////////////////
[25859]44//
45// AutoCaller* classes
46//
[14588]47////////////////////////////////////////////////////////////////////////////////
[6076]48
[51903]49
[14588]50/**
[51903]51 * Smart class that automatically increases the number of normal (non-limited)
52 * callers of the given VirtualBoxBase object when an instance is constructed
53 * and decreases it back when the created instance goes out of scope (i.e. gets
54 * destroyed).
[1]55 *
[98263]56 * If #hrc() returns a failure after the instance creation, it means that
[25859]57 * the managed VirtualBoxBase object is not Ready, or in any other invalid
58 * state, so that the caller must not use the object and can return this
59 * failed result code to the upper level.
[1]60 *
[51903]61 * See ObjectState::addCaller() and ObjectState::releaseCaller() for more
62 * details about object callers.
[13580]63 *
[51903]64 * A typical usage pattern to declare a normal method of some object (i.e. a
65 * method that is valid only when the object provides its full
66 * functionality) is:
67 * <code>
68 * STDMETHODIMP Component::Foo()
69 * {
70 * AutoCaller autoCaller(this);
[98262]71 * HRESULT hrc = autoCaller.hrc();
[51903]72 * if (SUCCEEDED(hrc))
73 * {
74 * ...
75 * }
76 * return hrc;
77 * }
78 * </code>
[13580]79 */
[51903]80class AutoCaller
[1]81{
82public:
[51903]83 /**
84 * Default constructor. Not terribly useful, but it's valid to create
85 * an instance without associating it with an object. It's a no-op,
86 * like the more useful constructor below when NULL is passed to it.
87 */
88 AutoCaller()
89 {
90 init(NULL, false);
91 }
[1]92
93 /**
[25859]94 * Increases the number of callers of the given object by calling
[51903]95 * ObjectState::addCaller() for the corresponding member instance.
[25859]96 *
[51903]97 * @param aObj Object to add a normal caller to. If NULL, this
[25859]98 * instance is effectively turned to no-op (where
[98263]99 * hrc() will return S_OK).
[25834]100 */
[51903]101 AutoCaller(VirtualBoxBase *aObj)
[25834]102 {
[51903]103 init(aObj, false);
[25834]104 }
105
106 /**
[25859]107 * If the number of callers was successfully increased, decreases it
[51903]108 * using ObjectState::releaseCaller(), otherwise does nothing.
[25834]109 */
[51903]110 ~AutoCaller()
[25834]111 {
[25859]112 if (mObj && SUCCEEDED(mRC))
[51903]113 mObj->getObjectState().releaseCaller();
[25834]114 }
115
116 /**
[98262]117 * Returns the stored result code returned by ObjectState::addCaller() after
118 * instance creation or after the last #add() call.
119 *
120 * A successful result code means the number of callers was successfully
121 * increased.
122 */
123 HRESULT hrc() const { return mRC; }
124
125 /**
[98263]126 * Returns |true| if |SUCCEEDED(hrc())| is |true|, for convenience. |true| means
127 * the number of callers was successfully increased.
[1]128 */
[25859]129 bool isOk() const { return SUCCEEDED(mRC); }
[1]130
131 /**
[98263]132 * Returns |true| if |FAILED(hrc())| is |true|, for convenience. |true| means
133 * the number of callers was _not_ successfully increased.
[94907]134 */
135 bool isNotOk() const { return FAILED(mRC); }
136
137 /**
[25859]138 * Temporarily decreases the number of callers of the managed object.
[98263]139 * May only be called if #isOk() returns |true|. Note that #hrc() will return
140 * E_FAIL after this method succeeds.
[1]141 */
[25859]142 void release()
[1]143 {
[25859]144 Assert(SUCCEEDED(mRC));
145 if (SUCCEEDED(mRC))
[1]146 {
[3033]147 if (mObj)
[51903]148 mObj->getObjectState().releaseCaller();
[25859]149 mRC = E_FAIL;
[1]150 }
[25859]151 }
[1]152
153 /**
[25859]154 * Restores the number of callers decreased by #release(). May only be
155 * called after #release().
[1]156 */
[25859]157 void add()
[1]158 {
[25859]159 Assert(!SUCCEEDED(mRC));
160 if (mObj && !SUCCEEDED(mRC))
[51903]161 mRC = mObj->getObjectState().addCaller(mLimited);
[25859]162 }
[1]163
164 /**
[25859]165 * Attaches another object to this caller instance.
166 * The previous object's caller is released before the new one is added.
[1]167 *
[25859]168 * @param aObj New object to attach, may be @c NULL.
[1]169 */
[25859]170 void attach(VirtualBoxBase *aObj)
[1]171 {
[25859]172 /* detect simple self-reattachment */
173 if (mObj != aObj)
174 {
175 if (mObj && SUCCEEDED(mRC))
176 release();
[28205]177 else if (!mObj)
178 {
179 /* Fix up the success state when nothing is attached. Otherwise
180 * there are a couple of assertion which would trigger. */
181 mRC = E_FAIL;
182 }
[25859]183 mObj = aObj;
184 add();
185 }
186 }
[1]187
[51903]188 /** Verbose equivalent to <tt>attach(NULL)</tt>. */
[25859]189 void detach() { attach(NULL); }
[1]190
[51903]191protected:
192 /**
193 * Internal constructor: Increases the number of callers of the given
194 * object (either normal or limited variant) by calling
195 * ObjectState::addCaller() for the corresponding member instance.
196 *
197 * @param aObj Object to add a caller to. If NULL, this
[98263]198 * instance is effectively turned to no-op (where hrc() will
199 * return S_OK).
[51903]200 * @param aLimited If |false|, then it's a regular caller, otherwise a
201 * limited caller.
202 */
203 void init(VirtualBoxBase *aObj, bool aLimited)
204 {
205 mObj = aObj;
[52489]206 mRC = S_OK;
207 mLimited = aLimited;
[51903]208 if (mObj)
209 mRC = mObj->getObjectState().addCaller(mLimited);
210 }
211
[1]212private:
[62636]213 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoCaller);
214 DECLARE_CLS_NEW_DELETE_NOOP(AutoCaller);
[1]215
[25859]216 VirtualBoxBase *mObj;
217 HRESULT mRC;
[51903]218 bool mLimited;
[1]219};
220
221/**
[25859]222 * Smart class that automatically increases the number of limited callers of
223 * the given VirtualBoxBase object when an instance is constructed and
224 * decreases it back when the created instance goes out of scope (i.e. gets
225 * destroyed).
[1]226 *
[25859]227 * A typical usage pattern to declare a limited method of some object (i.e.
228 * a method that is valid even if the object doesn't provide its full
229 * functionality) is:
230 * <code>
231 * STDMETHODIMP Component::Bar()
232 * {
233 * AutoLimitedCaller autoCaller(this);
[98262]234 * HRESULT hrc = autoCaller.hrc();
[39715]235 * if (SUCCEEDED(hrc))
236 * {
237 * ...
238 * }
239 * return hrc;
[25859]240 * </code>
[1]241 *
[51903]242 * See AutoCaller for more information about auto caller functionality.
[1]243 */
[51903]244class AutoLimitedCaller : public AutoCaller
245{
246public:
247 /**
248 * Default constructor. Not terribly useful, but it's valid to create
249 * an instance without associating it with an object. It's a no-op,
250 * like the more useful constructor below when NULL is passed to it.
251 */
252 AutoLimitedCaller()
253 {
254 AutoCaller::init(NULL, true);
255 }
[1]256
[51903]257 /**
258 * Increases the number of callers of the given object by calling
259 * ObjectState::addCaller() for the corresponding member instance.
260 *
261 * @param aObj Object to add a limited caller to. If NULL, this
[98263]262 * instance is effectively turned to no-op (where hrc() will
263 * return S_OK).
[51903]264 */
265 AutoLimitedCaller(VirtualBoxBase *aObj)
266 {
267 AutoCaller::init(aObj, true);
268 }
269
[63147]270private:
271 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoLimitedCaller); /* Shuts up MSC warning C4625. */
[51903]272};
273
[1]274/**
[25859]275 * Smart class to enclose the state transition NotReady->InInit->Ready.
[1]276 *
[25859]277 * The purpose of this span is to protect object initialization.
[1]278 *
[25859]279 * Instances must be created as a stack-based variable taking |this| pointer
280 * as the argument at the beginning of init() methods of VirtualBoxBase
281 * subclasses. When this variable is created it automatically places the
282 * object to the InInit state.
[1]283 *
[25859]284 * When the created variable goes out of scope (i.e. gets destroyed) then,
285 * depending on the result status of this initialization span, it either
286 * places the object to Ready or Limited state or calls the object's
287 * VirtualBoxBase::uninit() method which is supposed to place the object
288 * back to the NotReady state using the AutoUninitSpan class.
[1]289 *
[25859]290 * The initial result status of the initialization span is determined by the
291 * @a aResult argument of the AutoInitSpan constructor (Result::Failed by
292 * default). Inside the initialization span, the success status can be set
293 * to Result::Succeeded using #setSucceeded(), to to Result::Limited using
294 * #setLimited() or to Result::Failed using #setFailed(). Please don't
295 * forget to set the correct success status before getting the AutoInitSpan
296 * variable destroyed (for example, by performing an early return from
297 * the init() method)!
298 *
299 * Note that if an instance of this class gets constructed when the object
300 * is in the state other than NotReady, #isOk() returns |false| and methods
301 * of this class do nothing: the state transition is not performed.
302 *
303 * A typical usage pattern is:
304 * <code>
305 * HRESULT Component::init()
306 * {
[39715]307 * AutoInitSpan autoInitSpan(this);
308 * AssertReturn(autoInitSpan.isOk(), E_FAIL);
[25859]309 * ...
[26186]310 * if (FAILED(rc))
[25859]311 * return rc;
312 * ...
[26186]313 * if (SUCCEEDED(rc))
[25859]314 * autoInitSpan.setSucceeded();
315 * return rc;
316 * }
317 * </code>
318 *
319 * @note Never create instances of this class outside init() methods of
320 * VirtualBoxBase subclasses and never pass anything other than |this|
321 * as the argument to the constructor!
[1]322 */
[25859]323class AutoInitSpan
[1]324{
325public:
326
[25859]327 enum Result { Failed = 0x0, Succeeded = 0x1, Limited = 0x2 };
[1]328
[25859]329 AutoInitSpan(VirtualBoxBase *aObj, Result aResult = Failed);
330 ~AutoInitSpan();
[1]331
332 /**
[25859]333 * Returns |true| if this instance has been created at the right moment
334 * (when the object was in the NotReady state) and |false| otherwise.
[1]335 */
[25859]336 bool isOk() const { return mOk; }
[1]337
338 /**
[25859]339 * Sets the initialization status to Succeeded to indicates successful
340 * initialization. The AutoInitSpan destructor will place the managed
341 * VirtualBoxBase object to the Ready state.
[2672]342 */
[25859]343 void setSucceeded() { mResult = Succeeded; }
[2672]344
345 /**
[25859]346 * Sets the initialization status to Succeeded to indicate limited
347 * (partly successful) initialization. The AutoInitSpan destructor will
348 * place the managed VirtualBoxBase object to the Limited state.
[1]349 */
[25859]350 void setLimited() { mResult = Limited; }
[1]351
352 /**
[85929]353 * Sets the initialization status to Succeeded to indicate limited
354 * (partly successful) initialization but also adds the initialization
355 * error if required for further reporting. The AutoInitSpan destructor
356 * will place the managed VirtualBoxBase object to the Limited state.
357 */
358 void setLimited(HRESULT rc)
359 {
360 mResult = Limited;
361 mFailedRC = rc;
362 mpFailedEI = new ErrorInfo();
363 }
364
365 /**
[25859]366 * Sets the initialization status to Failure to indicates failed
367 * initialization. The AutoInitSpan destructor will place the managed
368 * VirtualBoxBase object to the InitFailed state and will automatically
369 * call its uninit() method which is supposed to place the object back
370 * to the NotReady state using AutoUninitSpan.
[2672]371 */
[59996]372 void setFailed(HRESULT rc = E_ACCESSDENIED)
373 {
374 mResult = Failed;
375 mFailedRC = rc;
376 mpFailedEI = new ErrorInfo();
377 }
[2672]378
[25859]379 /** Returns the current initialization result. */
380 Result result() { return mResult; }
[1]381
[25859]382private:
[2672]383
[62636]384 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoInitSpan);
385 DECLARE_CLS_NEW_DELETE_NOOP(AutoInitSpan);
[357]386
[25859]387 VirtualBoxBase *mObj;
388 Result mResult : 3; // must be at least total number of bits + 1 (sign)
389 bool mOk : 1;
[59996]390 HRESULT mFailedRC;
391 ErrorInfo *mpFailedEI;
[1]392};
393
394/**
[25859]395 * Smart class to enclose the state transition Limited->InInit->Ready.
[1]396 *
[25859]397 * The purpose of this span is to protect object re-initialization.
[6935]398 *
[25859]399 * Instances must be created as a stack-based variable taking |this| pointer
400 * as the argument at the beginning of methods of VirtualBoxBase
401 * subclasses that try to re-initialize the object to bring it to the Ready
402 * state (full functionality) after partial initialization (limited
403 * functionality). When this variable is created, it automatically places
404 * the object to the InInit state.
[6851]405 *
[25859]406 * When the created variable goes out of scope (i.e. gets destroyed),
407 * depending on the success status of this initialization span, it either
408 * places the object to the Ready state or brings it back to the Limited
409 * state.
[6851]410 *
[25859]411 * The initial success status of the re-initialization span is |false|. In
412 * order to make it successful, #setSucceeded() must be called before the
413 * instance is destroyed.
[13729]414 *
[25859]415 * Note that if an instance of this class gets constructed when the object
416 * is in the state other than Limited, #isOk() returns |false| and methods
417 * of this class do nothing: the state transition is not performed.
[6851]418 *
[25859]419 * A typical usage pattern is:
420 * <code>
421 * HRESULT Component::reinit()
422 * {
[51903]423 * AutoReinitSpan autoReinitSpan(this);
424 * AssertReturn(autoReinitSpan.isOk(), E_FAIL);
[25859]425 * ...
[26186]426 * if (FAILED(rc))
[25859]427 * return rc;
428 * ...
[26186]429 * if (SUCCEEDED(rc))
[25859]430 * autoReinitSpan.setSucceeded();
431 * return rc;
432 * }
433 * </code>
[6935]434 *
[25859]435 * @note Never create instances of this class outside re-initialization
436 * methods of VirtualBoxBase subclasses and never pass anything other than
437 * |this| as the argument to the constructor!
[1]438 */
[25859]439class AutoReinitSpan
[1]440{
[6851]441public:
442
[25859]443 AutoReinitSpan(VirtualBoxBase *aObj);
444 ~AutoReinitSpan();
[6851]445
446 /**
[25859]447 * Returns |true| if this instance has been created at the right moment
448 * (when the object was in the Limited state) and |false| otherwise.
[13729]449 */
[25859]450 bool isOk() const { return mOk; }
[13729]451
452 /**
[25859]453 * Sets the re-initialization status to Succeeded to indicates
454 * successful re-initialization. The AutoReinitSpan destructor will place
455 * the managed VirtualBoxBase object to the Ready state.
[6851]456 */
[25859]457 void setSucceeded() { mSucceeded = true; }
[6851]458
[25859]459private:
[13580]460
[62636]461 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoReinitSpan);
462 DECLARE_CLS_NEW_DELETE_NOOP(AutoReinitSpan);
[6851]463
[25859]464 VirtualBoxBase *mObj;
465 bool mSucceeded : 1;
466 bool mOk : 1;
[1]467};
468
469/**
[26984]470 * Smart class to enclose the state transition Ready->InUninit->NotReady,
471 * InitFailed->InUninit->NotReady.
[25859]472 *
473 * The purpose of this span is to protect object uninitialization.
474 *
475 * Instances must be created as a stack-based variable taking |this| pointer
476 * as the argument at the beginning of uninit() methods of VirtualBoxBase
477 * subclasses. When this variable is created it automatically places the
478 * object to the InUninit state, unless it is already in the NotReady state
479 * as indicated by #uninitDone() returning |true|. In the latter case, the
480 * uninit() method must immediately return because there should be nothing
481 * to uninitialize.
482 *
483 * When this variable goes out of scope (i.e. gets destroyed), it places the
484 * object to NotReady state.
485 *
486 * A typical usage pattern is:
487 * <code>
488 * void Component::uninit()
489 * {
[51903]490 * AutoUninitSpan autoUninitSpan(this);
[25859]491 * if (autoUninitSpan.uninitDone())
492 * return;
493 * ...
494 * }
495 * </code>
496 *
497 * @note The constructor of this class blocks the current thread execution
[51903]498 * until the number of callers added to the object using
499 * ObjectState::addCaller() or AutoCaller drops to zero. For this reason,
500 * it is forbidden to create instances of this class (or call uninit())
501 * within the AutoCaller or ObjectState::addCaller() scope because it is
502 * a guaranteed deadlock.
[25859]503 *
504 * @note Never create instances of this class outside uninit() methods and
505 * never pass anything other than |this| as the argument to the
506 * constructor!
[1]507 */
[25859]508class AutoUninitSpan
[1]509{
510public:
511
[75660]512 AutoUninitSpan(VirtualBoxBase *aObj, bool fTry = false);
[25859]513 ~AutoUninitSpan();
[1]514
[25859]515 /** |true| when uninit() is called as a result of init() failure */
516 bool initFailed() { return mInitFailed; }
[1]517
[25859]518 /** |true| when uninit() has already been called (so the object is NotReady) */
519 bool uninitDone() { return mUninitDone; }
[1]520
[75660]521 /** |true| when uninit() has failed, relevant only if it was a "try uninit" */
522 bool uninitFailed() { return mUninitFailed; }
523
[52095]524 void setSucceeded();
525
[25859]526private:
[1]527
[62636]528 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoUninitSpan);
529 DECLARE_CLS_NEW_DELETE_NOOP(AutoUninitSpan);
[1]530
[25859]531 VirtualBoxBase *mObj;
532 bool mInitFailed : 1;
533 bool mUninitDone : 1;
[75660]534 bool mUninitFailed : 1;
[1]535};
536
[76562]537#endif /* !MAIN_INCLUDED_AutoCaller_h */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use