VirtualBox

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

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

Main/AutoCaller.h: Add convenience wrapper isNotOk(), bugref:10223

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