summaryrefslogtreecommitdiff
path: root/src/VBox/Main/include/ObjectState.h
blob: baa98a79f9057d64104e0da4b7e7fa5bd467e01d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/* $Id$ */
/** @file
 *
 * VirtualBox object state handling definitions
 */

/*
 * Copyright (C) 2006-2017 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 */

#ifndef ____H_OBJECTSTATE
#define ____H_OBJECTSTATE
#ifndef RT_WITHOUT_PRAGMA_ONCE
# pragma once
#endif

#include "VBox/com/defs.h"
#include "VBox/com/AutoLock.h"
#include "VBox/com/ErrorInfo.h"

// Forward declaration needed, but nothing more.
class VirtualBoxBase;

////////////////////////////////////////////////////////////////////////////////
//
// ObjectState
//
////////////////////////////////////////////////////////////////////////////////

/**
 * Thec functionality implemented by this class is the primary object state
 * (used by VirtualBoxBase and thus part of all API classes) that indicates
 * if the object is ready to serve the calls, and if not, what stage it is
 * currently at. Here is the primary state diagram:
 *
 *              +-------------------------------------------------------+
 *              |                                                       |
 *              |         (InitFailed) -----------------------+         |
 *              |              ^                              |         |
 *              v              |                              v         |
 *  [*] ---> NotReady ----> (InInit) -----> Ready -----> (InUninit) ----+
 *                     ^       |
 *                     |       v
 *                     |    Limited
 *                     |       |
 *                     +-------+
 *
 * The object is fully operational only when its state is Ready. The Limited
 * state means that only some vital part of the object is operational, and it
 * requires some sort of reinitialization to become fully operational. The
 * NotReady state means the object is basically dead: it either was not yet
 * initialized after creation at all, or was uninitialized and is waiting to be
 * destroyed when the last reference to it is released. All other states are
 * transitional.
 *
 * The NotReady->InInit->Ready, NotReady->InInit->Limited and
 * NotReady->InInit->InitFailed transition is done by the AutoInitSpan smart
 * class.
 *
 * The Limited->InInit->Ready, Limited->InInit->Limited and
 * Limited->InInit->InitFailed transition is done by the AutoReinitSpan smart
 * class.
 *
 * The Ready->InUninit->NotReady and InitFailed->InUninit->NotReady
 * transitions are done by the AutoUninitSpan smart class.
 *
 * In order to maintain the primary state integrity and declared functionality
 * the following rules apply everywhere:
 *
 * 1) Use the above Auto*Span classes to perform state transitions. See the
 *    individual class descriptions for details.
 *
 * 2) All public methods of subclasses (i.e. all methods that can be called
 *    directly, not only from within other methods of the subclass) must have a
 *    standard prolog as described in the AutoCaller and AutoLimitedCaller
 *    documentation. Alternatively, they must use #addCaller() and
 *    #releaseCaller() directly (and therefore have both the prolog and the
 *    epilog), but this is not recommended because it is easy to forget the
 *    matching release, e.g. returning before reaching the call.
 */
class ObjectState
{
public:
    enum State { NotReady, Ready, InInit, InUninit, InitFailed, Limited };

    ObjectState(VirtualBoxBase *aObj);
    ~ObjectState();

    State getState();

    HRESULT addCaller(bool aLimited = false);
    void releaseCaller();

    bool autoInitSpanConstructor(State aExpectedState);
    void autoInitSpanDestructor(State aNewState, HRESULT aFailedRC, com::ErrorInfo *aFailedEI);
    State autoUninitSpanConstructor(bool fTry);
    void autoUninitSpanDestructor();

private:
    ObjectState();

    void setState(State aState);

    /** Pointer to the managed object, mostly for error signalling or debugging
     * purposes, not used much. Guaranteed to be valid during the lifetime of
     * this object, no need to mess with refcount. */
    VirtualBoxBase *mObj;
    /** Primary state of this object */
    State mState;
    /** Thread that caused the last state change */
    RTTHREAD mStateChangeThread;
    /** Result code for failed object initialization */
    HRESULT mFailedRC;
    /** Error information for failed object initialization */
    com::ErrorInfo *mpFailedEI;
    /** Total number of active calls to this object */
    unsigned mCallers;
    /** Posted when the number of callers drops to zero */
    RTSEMEVENT mZeroCallersSem;
    /** Posted when the object goes from InInit/InUninit to some other state */
    RTSEMEVENTMULTI mInitUninitSem;
    /** Number of threads waiting for mInitUninitDoneSem */
    unsigned mInitUninitWaiters;

    /** Protects access to state related data members */
    util::RWLockHandle mStateLock;

private:
    DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(ObjectState); /* Shuts up MSC warning C4625. */
};

#endif // !____H_OBJECTSTATE