VirtualBox

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

Last change on this file since 85007 was 82968, checked in by vboxsync, 5 years 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.0 KB
Line 
1/* $Id: AutoCaller.h 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox object caller handling definitions
5 */
6
7/*
8 * Copyright (C) 2006-2020 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 Failure to indicates failed
336 * initialization. The AutoInitSpan destructor will place the managed
337 * VirtualBoxBase object to the InitFailed state and will automatically
338 * call its uninit() method which is supposed to place the object back
339 * to the NotReady state using AutoUninitSpan.
340 */
341 void setFailed(HRESULT rc = E_ACCESSDENIED)
342 {
343 mResult = Failed;
344 mFailedRC = rc;
345 mpFailedEI = new ErrorInfo();
346 }
347
348 /** Returns the current initialization result. */
349 Result result() { return mResult; }
350
351private:
352
353 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoInitSpan);
354 DECLARE_CLS_NEW_DELETE_NOOP(AutoInitSpan);
355
356 VirtualBoxBase *mObj;
357 Result mResult : 3; // must be at least total number of bits + 1 (sign)
358 bool mOk : 1;
359 HRESULT mFailedRC;
360 ErrorInfo *mpFailedEI;
361};
362
363/**
364 * Smart class to enclose the state transition Limited->InInit->Ready.
365 *
366 * The purpose of this span is to protect object re-initialization.
367 *
368 * Instances must be created as a stack-based variable taking |this| pointer
369 * as the argument at the beginning of methods of VirtualBoxBase
370 * subclasses that try to re-initialize the object to bring it to the Ready
371 * state (full functionality) after partial initialization (limited
372 * functionality). When this variable is created, it automatically places
373 * the object to the InInit state.
374 *
375 * When the created variable goes out of scope (i.e. gets destroyed),
376 * depending on the success status of this initialization span, it either
377 * places the object to the Ready state or brings it back to the Limited
378 * state.
379 *
380 * The initial success status of the re-initialization span is |false|. In
381 * order to make it successful, #setSucceeded() must be called before the
382 * instance is destroyed.
383 *
384 * Note that if an instance of this class gets constructed when the object
385 * is in the state other than Limited, #isOk() returns |false| and methods
386 * of this class do nothing: the state transition is not performed.
387 *
388 * A typical usage pattern is:
389 * <code>
390 * HRESULT Component::reinit()
391 * {
392 * AutoReinitSpan autoReinitSpan(this);
393 * AssertReturn(autoReinitSpan.isOk(), E_FAIL);
394 * ...
395 * if (FAILED(rc))
396 * return rc;
397 * ...
398 * if (SUCCEEDED(rc))
399 * autoReinitSpan.setSucceeded();
400 * return rc;
401 * }
402 * </code>
403 *
404 * @note Never create instances of this class outside re-initialization
405 * methods of VirtualBoxBase subclasses and never pass anything other than
406 * |this| as the argument to the constructor!
407 */
408class AutoReinitSpan
409{
410public:
411
412 AutoReinitSpan(VirtualBoxBase *aObj);
413 ~AutoReinitSpan();
414
415 /**
416 * Returns |true| if this instance has been created at the right moment
417 * (when the object was in the Limited state) and |false| otherwise.
418 */
419 bool isOk() const { return mOk; }
420
421 /**
422 * Sets the re-initialization status to Succeeded to indicates
423 * successful re-initialization. The AutoReinitSpan destructor will place
424 * the managed VirtualBoxBase object to the Ready state.
425 */
426 void setSucceeded() { mSucceeded = true; }
427
428private:
429
430 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoReinitSpan);
431 DECLARE_CLS_NEW_DELETE_NOOP(AutoReinitSpan);
432
433 VirtualBoxBase *mObj;
434 bool mSucceeded : 1;
435 bool mOk : 1;
436};
437
438/**
439 * Smart class to enclose the state transition Ready->InUninit->NotReady,
440 * InitFailed->InUninit->NotReady.
441 *
442 * The purpose of this span is to protect object uninitialization.
443 *
444 * Instances must be created as a stack-based variable taking |this| pointer
445 * as the argument at the beginning of uninit() methods of VirtualBoxBase
446 * subclasses. When this variable is created it automatically places the
447 * object to the InUninit state, unless it is already in the NotReady state
448 * as indicated by #uninitDone() returning |true|. In the latter case, the
449 * uninit() method must immediately return because there should be nothing
450 * to uninitialize.
451 *
452 * When this variable goes out of scope (i.e. gets destroyed), it places the
453 * object to NotReady state.
454 *
455 * A typical usage pattern is:
456 * <code>
457 * void Component::uninit()
458 * {
459 * AutoUninitSpan autoUninitSpan(this);
460 * if (autoUninitSpan.uninitDone())
461 * return;
462 * ...
463 * }
464 * </code>
465 *
466 * @note The constructor of this class blocks the current thread execution
467 * until the number of callers added to the object using
468 * ObjectState::addCaller() or AutoCaller drops to zero. For this reason,
469 * it is forbidden to create instances of this class (or call uninit())
470 * within the AutoCaller or ObjectState::addCaller() scope because it is
471 * a guaranteed deadlock.
472 *
473 * @note Never create instances of this class outside uninit() methods and
474 * never pass anything other than |this| as the argument to the
475 * constructor!
476 */
477class AutoUninitSpan
478{
479public:
480
481 AutoUninitSpan(VirtualBoxBase *aObj, bool fTry = false);
482 ~AutoUninitSpan();
483
484 /** |true| when uninit() is called as a result of init() failure */
485 bool initFailed() { return mInitFailed; }
486
487 /** |true| when uninit() has already been called (so the object is NotReady) */
488 bool uninitDone() { return mUninitDone; }
489
490 /** |true| when uninit() has failed, relevant only if it was a "try uninit" */
491 bool uninitFailed() { return mUninitFailed; }
492
493 void setSucceeded();
494
495private:
496
497 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoUninitSpan);
498 DECLARE_CLS_NEW_DELETE_NOOP(AutoUninitSpan);
499
500 VirtualBoxBase *mObj;
501 bool mInitFailed : 1;
502 bool mUninitDone : 1;
503 bool mUninitFailed : 1;
504};
505
506#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