VirtualBox

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

Last change on this file since 94521 was 93115, checked in by vboxsync, 2 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use