VirtualBox

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

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

Copyright year updates by scm.

  • 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 98103 2023-01-17 14:15:46Z 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 #rc() 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.rc();
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 * rc() 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()
118 * after instance creation or after the last #add() call. A successful
119 * result code means the number of callers was successfully increased.
120 */
121 HRESULT rc() const { return mRC; }
122
123 /**
124 * Returns |true| if |SUCCEEDED(rc())| is |true|, for convenience.
125 * |true| means the number of callers was successfully increased.
126 */
127 bool isOk() const { return SUCCEEDED(mRC); }
128
129 /**
130 * Returns |true| if |FAILED(rc())| is |true|, for convenience.
131 * |true| means the number of callers was _not_ successfully increased.
132 */
133 bool isNotOk() const { return FAILED(mRC); }
134
135 /**
136 * Temporarily decreases the number of callers of the managed object.
137 * May only be called if #isOk() returns |true|. Note that #rc() will
138 * return E_FAIL after this method succeeds.
139 */
140 void release()
141 {
142 Assert(SUCCEEDED(mRC));
143 if (SUCCEEDED(mRC))
144 {
145 if (mObj)
146 mObj->getObjectState().releaseCaller();
147 mRC = E_FAIL;
148 }
149 }
150
151 /**
152 * Restores the number of callers decreased by #release(). May only be
153 * called after #release().
154 */
155 void add()
156 {
157 Assert(!SUCCEEDED(mRC));
158 if (mObj && !SUCCEEDED(mRC))
159 mRC = mObj->getObjectState().addCaller(mLimited);
160 }
161
162 /**
163 * Attaches another object to this caller instance.
164 * The previous object's caller is released before the new one is added.
165 *
166 * @param aObj New object to attach, may be @c NULL.
167 */
168 void attach(VirtualBoxBase *aObj)
169 {
170 /* detect simple self-reattachment */
171 if (mObj != aObj)
172 {
173 if (mObj && SUCCEEDED(mRC))
174 release();
175 else if (!mObj)
176 {
177 /* Fix up the success state when nothing is attached. Otherwise
178 * there are a couple of assertion which would trigger. */
179 mRC = E_FAIL;
180 }
181 mObj = aObj;
182 add();
183 }
184 }
185
186 /** Verbose equivalent to <tt>attach(NULL)</tt>. */
187 void detach() { attach(NULL); }
188
189protected:
190 /**
191 * Internal constructor: Increases the number of callers of the given
192 * object (either normal or limited variant) by calling
193 * ObjectState::addCaller() for the corresponding member instance.
194 *
195 * @param aObj Object to add a caller to. If NULL, this
196 * instance is effectively turned to no-op (where
197 * rc() will return S_OK).
198 * @param aLimited If |false|, then it's a regular caller, otherwise a
199 * limited caller.
200 */
201 void init(VirtualBoxBase *aObj, bool aLimited)
202 {
203 mObj = aObj;
204 mRC = S_OK;
205 mLimited = aLimited;
206 if (mObj)
207 mRC = mObj->getObjectState().addCaller(mLimited);
208 }
209
210private:
211 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoCaller);
212 DECLARE_CLS_NEW_DELETE_NOOP(AutoCaller);
213
214 VirtualBoxBase *mObj;
215 HRESULT mRC;
216 bool mLimited;
217};
218
219/**
220 * Smart class that automatically increases the number of limited callers of
221 * the given VirtualBoxBase object when an instance is constructed and
222 * decreases it back when the created instance goes out of scope (i.e. gets
223 * destroyed).
224 *
225 * A typical usage pattern to declare a limited method of some object (i.e.
226 * a method that is valid even if the object doesn't provide its full
227 * functionality) is:
228 * <code>
229 * STDMETHODIMP Component::Bar()
230 * {
231 * AutoLimitedCaller autoCaller(this);
232 * HRESULT hrc = autoCaller.rc();
233 * if (SUCCEEDED(hrc))
234 * {
235 * ...
236 * }
237 * return hrc;
238 * </code>
239 *
240 * See AutoCaller for more information about auto caller functionality.
241 */
242class AutoLimitedCaller : public AutoCaller
243{
244public:
245 /**
246 * Default constructor. Not terribly useful, but it's valid to create
247 * an instance without associating it with an object. It's a no-op,
248 * like the more useful constructor below when NULL is passed to it.
249 */
250 AutoLimitedCaller()
251 {
252 AutoCaller::init(NULL, true);
253 }
254
255 /**
256 * Increases the number of callers of the given object by calling
257 * ObjectState::addCaller() for the corresponding member instance.
258 *
259 * @param aObj Object to add a limited caller to. If NULL, this
260 * instance is effectively turned to no-op (where
261 * rc() will return S_OK).
262 */
263 AutoLimitedCaller(VirtualBoxBase *aObj)
264 {
265 AutoCaller::init(aObj, true);
266 }
267
268private:
269 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoLimitedCaller); /* Shuts up MSC warning C4625. */
270};
271
272/**
273 * Smart class to enclose the state transition NotReady->InInit->Ready.
274 *
275 * The purpose of this span is to protect object initialization.
276 *
277 * Instances must be created as a stack-based variable taking |this| pointer
278 * as the argument at the beginning of init() methods of VirtualBoxBase
279 * subclasses. When this variable is created it automatically places the
280 * object to the InInit state.
281 *
282 * When the created variable goes out of scope (i.e. gets destroyed) then,
283 * depending on the result status of this initialization span, it either
284 * places the object to Ready or Limited state or calls the object's
285 * VirtualBoxBase::uninit() method which is supposed to place the object
286 * back to the NotReady state using the AutoUninitSpan class.
287 *
288 * The initial result status of the initialization span is determined by the
289 * @a aResult argument of the AutoInitSpan constructor (Result::Failed by
290 * default). Inside the initialization span, the success status can be set
291 * to Result::Succeeded using #setSucceeded(), to to Result::Limited using
292 * #setLimited() or to Result::Failed using #setFailed(). Please don't
293 * forget to set the correct success status before getting the AutoInitSpan
294 * variable destroyed (for example, by performing an early return from
295 * the init() method)!
296 *
297 * Note that if an instance of this class gets constructed when the object
298 * is in the state other than NotReady, #isOk() returns |false| and methods
299 * of this class do nothing: the state transition is not performed.
300 *
301 * A typical usage pattern is:
302 * <code>
303 * HRESULT Component::init()
304 * {
305 * AutoInitSpan autoInitSpan(this);
306 * AssertReturn(autoInitSpan.isOk(), E_FAIL);
307 * ...
308 * if (FAILED(rc))
309 * return rc;
310 * ...
311 * if (SUCCEEDED(rc))
312 * autoInitSpan.setSucceeded();
313 * return rc;
314 * }
315 * </code>
316 *
317 * @note Never create instances of this class outside init() methods of
318 * VirtualBoxBase subclasses and never pass anything other than |this|
319 * as the argument to the constructor!
320 */
321class AutoInitSpan
322{
323public:
324
325 enum Result { Failed = 0x0, Succeeded = 0x1, Limited = 0x2 };
326
327 AutoInitSpan(VirtualBoxBase *aObj, Result aResult = Failed);
328 ~AutoInitSpan();
329
330 /**
331 * Returns |true| if this instance has been created at the right moment
332 * (when the object was in the NotReady state) and |false| otherwise.
333 */
334 bool isOk() const { return mOk; }
335
336 /**
337 * Sets the initialization status to Succeeded to indicates successful
338 * initialization. The AutoInitSpan destructor will place the managed
339 * VirtualBoxBase object to the Ready state.
340 */
341 void setSucceeded() { mResult = Succeeded; }
342
343 /**
344 * Sets the initialization status to Succeeded to indicate limited
345 * (partly successful) initialization. The AutoInitSpan destructor will
346 * place the managed VirtualBoxBase object to the Limited state.
347 */
348 void setLimited() { mResult = Limited; }
349
350 /**
351 * Sets the initialization status to Succeeded to indicate limited
352 * (partly successful) initialization but also adds the initialization
353 * error if required for further reporting. The AutoInitSpan destructor
354 * will place the managed VirtualBoxBase object to the Limited state.
355 */
356 void setLimited(HRESULT rc)
357 {
358 mResult = Limited;
359 mFailedRC = rc;
360 mpFailedEI = new ErrorInfo();
361 }
362
363 /**
364 * Sets the initialization status to Failure to indicates failed
365 * initialization. The AutoInitSpan destructor will place the managed
366 * VirtualBoxBase object to the InitFailed state and will automatically
367 * call its uninit() method which is supposed to place the object back
368 * to the NotReady state using AutoUninitSpan.
369 */
370 void setFailed(HRESULT rc = E_ACCESSDENIED)
371 {
372 mResult = Failed;
373 mFailedRC = rc;
374 mpFailedEI = new ErrorInfo();
375 }
376
377 /** Returns the current initialization result. */
378 Result result() { return mResult; }
379
380private:
381
382 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoInitSpan);
383 DECLARE_CLS_NEW_DELETE_NOOP(AutoInitSpan);
384
385 VirtualBoxBase *mObj;
386 Result mResult : 3; // must be at least total number of bits + 1 (sign)
387 bool mOk : 1;
388 HRESULT mFailedRC;
389 ErrorInfo *mpFailedEI;
390};
391
392/**
393 * Smart class to enclose the state transition Limited->InInit->Ready.
394 *
395 * The purpose of this span is to protect object re-initialization.
396 *
397 * Instances must be created as a stack-based variable taking |this| pointer
398 * as the argument at the beginning of methods of VirtualBoxBase
399 * subclasses that try to re-initialize the object to bring it to the Ready
400 * state (full functionality) after partial initialization (limited
401 * functionality). When this variable is created, it automatically places
402 * the object to the InInit state.
403 *
404 * When the created variable goes out of scope (i.e. gets destroyed),
405 * depending on the success status of this initialization span, it either
406 * places the object to the Ready state or brings it back to the Limited
407 * state.
408 *
409 * The initial success status of the re-initialization span is |false|. In
410 * order to make it successful, #setSucceeded() must be called before the
411 * instance is destroyed.
412 *
413 * Note that if an instance of this class gets constructed when the object
414 * is in the state other than Limited, #isOk() returns |false| and methods
415 * of this class do nothing: the state transition is not performed.
416 *
417 * A typical usage pattern is:
418 * <code>
419 * HRESULT Component::reinit()
420 * {
421 * AutoReinitSpan autoReinitSpan(this);
422 * AssertReturn(autoReinitSpan.isOk(), E_FAIL);
423 * ...
424 * if (FAILED(rc))
425 * return rc;
426 * ...
427 * if (SUCCEEDED(rc))
428 * autoReinitSpan.setSucceeded();
429 * return rc;
430 * }
431 * </code>
432 *
433 * @note Never create instances of this class outside re-initialization
434 * methods of VirtualBoxBase subclasses and never pass anything other than
435 * |this| as the argument to the constructor!
436 */
437class AutoReinitSpan
438{
439public:
440
441 AutoReinitSpan(VirtualBoxBase *aObj);
442 ~AutoReinitSpan();
443
444 /**
445 * Returns |true| if this instance has been created at the right moment
446 * (when the object was in the Limited state) and |false| otherwise.
447 */
448 bool isOk() const { return mOk; }
449
450 /**
451 * Sets the re-initialization status to Succeeded to indicates
452 * successful re-initialization. The AutoReinitSpan destructor will place
453 * the managed VirtualBoxBase object to the Ready state.
454 */
455 void setSucceeded() { mSucceeded = true; }
456
457private:
458
459 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoReinitSpan);
460 DECLARE_CLS_NEW_DELETE_NOOP(AutoReinitSpan);
461
462 VirtualBoxBase *mObj;
463 bool mSucceeded : 1;
464 bool mOk : 1;
465};
466
467/**
468 * Smart class to enclose the state transition Ready->InUninit->NotReady,
469 * InitFailed->InUninit->NotReady.
470 *
471 * The purpose of this span is to protect object uninitialization.
472 *
473 * Instances must be created as a stack-based variable taking |this| pointer
474 * as the argument at the beginning of uninit() methods of VirtualBoxBase
475 * subclasses. When this variable is created it automatically places the
476 * object to the InUninit state, unless it is already in the NotReady state
477 * as indicated by #uninitDone() returning |true|. In the latter case, the
478 * uninit() method must immediately return because there should be nothing
479 * to uninitialize.
480 *
481 * When this variable goes out of scope (i.e. gets destroyed), it places the
482 * object to NotReady state.
483 *
484 * A typical usage pattern is:
485 * <code>
486 * void Component::uninit()
487 * {
488 * AutoUninitSpan autoUninitSpan(this);
489 * if (autoUninitSpan.uninitDone())
490 * return;
491 * ...
492 * }
493 * </code>
494 *
495 * @note The constructor of this class blocks the current thread execution
496 * until the number of callers added to the object using
497 * ObjectState::addCaller() or AutoCaller drops to zero. For this reason,
498 * it is forbidden to create instances of this class (or call uninit())
499 * within the AutoCaller or ObjectState::addCaller() scope because it is
500 * a guaranteed deadlock.
501 *
502 * @note Never create instances of this class outside uninit() methods and
503 * never pass anything other than |this| as the argument to the
504 * constructor!
505 */
506class AutoUninitSpan
507{
508public:
509
510 AutoUninitSpan(VirtualBoxBase *aObj, bool fTry = false);
511 ~AutoUninitSpan();
512
513 /** |true| when uninit() is called as a result of init() failure */
514 bool initFailed() { return mInitFailed; }
515
516 /** |true| when uninit() has already been called (so the object is NotReady) */
517 bool uninitDone() { return mUninitDone; }
518
519 /** |true| when uninit() has failed, relevant only if it was a "try uninit" */
520 bool uninitFailed() { return mUninitFailed; }
521
522 void setSucceeded();
523
524private:
525
526 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoUninitSpan);
527 DECLARE_CLS_NEW_DELETE_NOOP(AutoUninitSpan);
528
529 VirtualBoxBase *mObj;
530 bool mInitFailed : 1;
531 bool mUninitDone : 1;
532 bool mUninitFailed : 1;
533};
534
535#endif /* !MAIN_INCLUDED_AutoCaller_h */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use