VirtualBox

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

Last change on this file since 53517 was 52489, checked in by vboxsync, 10 years ago

Main: tabs

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