VirtualBox

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

Last change on this file was 98263, checked in by vboxsync, 15 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
Line 
1/* $Id: AutoCaller.h 98263 2023-01-24 01:52:49Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox object caller handling definitions
5 */
6
7/*
8 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
9 *
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
27 */
28
29#ifndef MAIN_INCLUDED_AutoCaller_h
30#define MAIN_INCLUDED_AutoCaller_h
31#ifndef RT_WITHOUT_PRAGMA_ONCE
32# pragma once
33#endif
34
35#include "ObjectState.h"
36
37#include "VBox/com/AutoLock.h"
38
39// Forward declaration needed, but nothing more.
40class VirtualBoxBase;
41
42
43////////////////////////////////////////////////////////////////////////////////
44//
45// AutoCaller* classes
46//
47////////////////////////////////////////////////////////////////////////////////
48
49
50/**
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).
55 *
56 * If #hrc() returns a failure after the instance creation, it means that
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.
60 *
61 * See ObjectState::addCaller() and ObjectState::releaseCaller() for more
62 * details about object callers.
63 *
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);
71 * HRESULT hrc = autoCaller.hrc();
72 * if (SUCCEEDED(hrc))
73 * {
74 * ...
75 * }
76 * return hrc;
77 * }
78 * </code>
79 */
80class AutoCaller
81{
82public:
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 }
92
93 /**
94 * Increases the number of callers of the given object by calling
95 * ObjectState::addCaller() for the corresponding member instance.
96 *
97 * @param aObj Object to add a normal caller to. If NULL, this
98 * instance is effectively turned to no-op (where
99 * hrc() will return S_OK).
100 */
101 AutoCaller(VirtualBoxBase *aObj)
102 {
103 init(aObj, false);
104 }
105
106 /**
107 * If the number of callers was successfully increased, decreases it
108 * using ObjectState::releaseCaller(), otherwise does nothing.
109 */
110 ~AutoCaller()
111 {
112 if (mObj && SUCCEEDED(mRC))
113 mObj->getObjectState().releaseCaller();
114 }
115
116 /**
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 /**
126 * Returns |true| if |SUCCEEDED(hrc())| is |true|, for convenience. |true| means
127 * the number of callers was successfully increased.
128 */
129 bool isOk() const { return SUCCEEDED(mRC); }
130
131 /**
132 * Returns |true| if |FAILED(hrc())| is |true|, for convenience. |true| means
133 * the number of callers was _not_ successfully increased.
134 */
135 bool isNotOk() const { return FAILED(mRC); }
136
137 /**
138 * Temporarily decreases the number of callers of the managed object.
139 * May only be called if #isOk() returns |true|. Note that #hrc() will return
140 * E_FAIL after this method succeeds.
141 */
142 void release()
143 {
144 Assert(SUCCEEDED(mRC));
145 if (SUCCEEDED(mRC))
146 {
147 if (mObj)
148 mObj->getObjectState().releaseCaller();
149 mRC = E_FAIL;
150 }
151 }
152
153 /**
154 * Restores the number of callers decreased by #release(). May only be
155 * called after #release().
156 */
157 void add()
158 {
159 Assert(!SUCCEEDED(mRC));
160 if (mObj && !SUCCEEDED(mRC))
161 mRC = mObj->getObjectState().addCaller(mLimited);
162 }
163
164 /**
165 * Attaches another object to this caller instance.
166 * The previous object's caller is released before the new one is added.
167 *
168 * @param aObj New object to attach, may be @c NULL.
169 */
170 void attach(VirtualBoxBase *aObj)
171 {
172 /* detect simple self-reattachment */
173 if (mObj != aObj)
174 {
175 if (mObj && SUCCEEDED(mRC))
176 release();
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 }
183 mObj = aObj;
184 add();
185 }
186 }
187
188 /** Verbose equivalent to <tt>attach(NULL)</tt>. */
189 void detach() { attach(NULL); }
190
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
198 * instance is effectively turned to no-op (where hrc() will
199 * return S_OK).
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;
206 mRC = S_OK;
207 mLimited = aLimited;
208 if (mObj)
209 mRC = mObj->getObjectState().addCaller(mLimited);
210 }
211
212private:
213 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoCaller);
214 DECLARE_CLS_NEW_DELETE_NOOP(AutoCaller);
215
216 VirtualBoxBase *mObj;
217 HRESULT mRC;
218 bool mLimited;
219};
220
221/**
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).
226 *
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);
234 * HRESULT hrc = autoCaller.hrc();
235 * if (SUCCEEDED(hrc))
236 * {
237 * ...
238 * }
239 * return hrc;
240 * </code>
241 *
242 * See AutoCaller for more information about auto caller functionality.
243 */
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 }
256
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
262 * instance is effectively turned to no-op (where hrc() will
263 * return S_OK).
264 */
265 AutoLimitedCaller(VirtualBoxBase *aObj)
266 {
267 AutoCaller::init(aObj, true);
268 }
269
270private:
271 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoLimitedCaller); /* Shuts up MSC warning C4625. */
272};
273
274/**
275 * Smart class to enclose the state transition NotReady->InInit->Ready.
276 *
277 * The purpose of this span is to protect object initialization.
278 *
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.
283 *
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.
289 *
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 * {
307 * AutoInitSpan autoInitSpan(this);
308 * AssertReturn(autoInitSpan.isOk(), E_FAIL);
309 * ...
310 * if (FAILED(rc))
311 * return rc;
312 * ...
313 * if (SUCCEEDED(rc))
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!
322 */
323class AutoInitSpan
324{
325public:
326
327 enum Result { Failed = 0x0, Succeeded = 0x1, Limited = 0x2 };
328
329 AutoInitSpan(VirtualBoxBase *aObj, Result aResult = Failed);
330 ~AutoInitSpan();
331
332 /**
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.
335 */
336 bool isOk() const { return mOk; }
337
338 /**
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.
342 */
343 void setSucceeded() { mResult = Succeeded; }
344
345 /**
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.
349 */
350 void setLimited() { mResult = Limited; }
351
352 /**
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 /**
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.
371 */
372 void setFailed(HRESULT rc = E_ACCESSDENIED)
373 {
374 mResult = Failed;
375 mFailedRC = rc;
376 mpFailedEI = new ErrorInfo();
377 }
378
379 /** Returns the current initialization result. */
380 Result result() { return mResult; }
381
382private:
383
384 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoInitSpan);
385 DECLARE_CLS_NEW_DELETE_NOOP(AutoInitSpan);
386
387 VirtualBoxBase *mObj;
388 Result mResult : 3; // must be at least total number of bits + 1 (sign)
389 bool mOk : 1;
390 HRESULT mFailedRC;
391 ErrorInfo *mpFailedEI;
392};
393
394/**
395 * Smart class to enclose the state transition Limited->InInit->Ready.
396 *
397 * The purpose of this span is to protect object re-initialization.
398 *
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.
405 *
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.
410 *
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.
414 *
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.
418 *
419 * A typical usage pattern is:
420 * <code>
421 * HRESULT Component::reinit()
422 * {
423 * AutoReinitSpan autoReinitSpan(this);
424 * AssertReturn(autoReinitSpan.isOk(), E_FAIL);
425 * ...
426 * if (FAILED(rc))
427 * return rc;
428 * ...
429 * if (SUCCEEDED(rc))
430 * autoReinitSpan.setSucceeded();
431 * return rc;
432 * }
433 * </code>
434 *
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!
438 */
439class AutoReinitSpan
440{
441public:
442
443 AutoReinitSpan(VirtualBoxBase *aObj);
444 ~AutoReinitSpan();
445
446 /**
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.
449 */
450 bool isOk() const { return mOk; }
451
452 /**
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.
456 */
457 void setSucceeded() { mSucceeded = true; }
458
459private:
460
461 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoReinitSpan);
462 DECLARE_CLS_NEW_DELETE_NOOP(AutoReinitSpan);
463
464 VirtualBoxBase *mObj;
465 bool mSucceeded : 1;
466 bool mOk : 1;
467};
468
469/**
470 * Smart class to enclose the state transition Ready->InUninit->NotReady,
471 * InitFailed->InUninit->NotReady.
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 * {
490 * AutoUninitSpan autoUninitSpan(this);
491 * if (autoUninitSpan.uninitDone())
492 * return;
493 * ...
494 * }
495 * </code>
496 *
497 * @note The constructor of this class blocks the current thread execution
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.
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!
507 */
508class AutoUninitSpan
509{
510public:
511
512 AutoUninitSpan(VirtualBoxBase *aObj, bool fTry = false);
513 ~AutoUninitSpan();
514
515 /** |true| when uninit() is called as a result of init() failure */
516 bool initFailed() { return mInitFailed; }
517
518 /** |true| when uninit() has already been called (so the object is NotReady) */
519 bool uninitDone() { return mUninitDone; }
520
521 /** |true| when uninit() has failed, relevant only if it was a "try uninit" */
522 bool uninitFailed() { return mUninitFailed; }
523
524 void setSucceeded();
525
526private:
527
528 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoUninitSpan);
529 DECLARE_CLS_NEW_DELETE_NOOP(AutoUninitSpan);
530
531 VirtualBoxBase *mObj;
532 bool mInitFailed : 1;
533 bool mUninitDone : 1;
534 bool mUninitFailed : 1;
535};
536
537#endif /* !MAIN_INCLUDED_AutoCaller_h */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use