summaryrefslogtreecommitdiff
path: root/include/VBox/com/MultiResult.h
blob: 705e1dbef0a31f428c7139431bec1942a08f1e4b (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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
/* $Id: MultiResult.h $ */

/** @file
 * MS COM / XPCOM Abstraction Layer:
 * MultiResult class declarations
 */

/*
 * Copyright (C) 2008-2012 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.
 *
 * The contents of this file may alternatively be used under the terms
 * of the Common Development and Distribution License Version 1.0
 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
 * VirtualBox OSE distribution, in which case the provisions of the
 * CDDL are applicable instead of those of the GPL.
 *
 * You may elect to license modified versions of this file under the
 * terms and conditions of either the GPL or the CDDL or both.
 */

#ifndef ___VBox_com_MultiResult_h
#define ___VBox_com_MultiResult_h

#include "VBox/com/defs.h"
#include "VBox/com/string.h"

#include <stdarg.h>

namespace com
{

/**
 * "First worst" result type.
 *
 * Variables of this class are used instead of HRESULT variables when it is
 * desirable to memorize the "first worst" result code instead of the last
 * assigned one. In other words, an assignment operation to a variable of this
 * class will succeed only if the result code to assign has worse severity. The
 * following table demonstrate this (the first column lists the previous result
 * code stored in the variable, the first row lists the new result code being
 * assigned, 'A' means the assignment will take place, '> S_OK' means a warning
 * result code):
 *
 * {{{
 *             FAILED    > S_OK    S_OK
 * FAILED        -         -         -
 * > S_OK        A         -         -
 * S_OK          A         A         -
 *
 * }}}
 *
 * In practice, you will need to use a FWResult variable when you call some COM
 * method B after another COM method A fails and want to return the result code
 * of A even if B also fails, but want to return the failed result code of B if
 * A issues a warning or succeeds.
 */
class FWResult
{

public:

    /**
     * Constructs a new variable. Note that by default this constructor sets the
     * result code to E_FAIL to make sure a failure is returned to the caller if
     * the variable is never assigned another value (which is considered as the
     * improper use of this class).
     */
    FWResult (HRESULT aRC = E_FAIL) : mRC (aRC) {}

    FWResult &operator= (HRESULT aRC)
    {
        if ((FAILED (aRC) && !FAILED (mRC)) ||
            (mRC == S_OK && aRC != S_OK))
            mRC = aRC;

        return *this;
    }

    operator HRESULT() const { return mRC; }

    HRESULT *operator&() { return &mRC; }

private:

    HRESULT mRC;
};

/**
 * The MultiResult class is a com::FWResult enhancement that also acts as a
 * switch to turn on multi-error mode for VirtualBoxBase::setError() and
 * VirtualBoxBase::setWarning() calls.
 *
 * When an instance of this class is created, multi-error mode is turned on
 * for the current thread and the turn-on counter is increased by one. In
 * multi-error mode, a call to setError() or setWarning() does not
 * overwrite the current error or warning info object possibly set on the
 * current thread by other method calls, but instead it stores this old
 * object in the IVirtualBoxErrorInfo::next attribute of the new error
 * object being set.
 *
 * This way, error/warning objects are stacked together and form a chain of
 * errors where the most recent error is the first one retrieved by the
 * calling party, the preceding error is what the
 * IVirtualBoxErrorInfo::next attribute of the first error points to, and so
 * on, up to the first error or warning occurred which is the last in the
 * chain. See IVirtualBoxErrorInfo documentation for more info.
 *
 * When the instance of the MultiResult class goes out of scope and gets
 * destroyed, it automatically decreases the turn-on counter by one. If
 * the counter drops to zero, multi-error mode for the current thread is
 * turned off and the thread switches back to single-error mode where every
 * next error or warning object overwrites the previous one.
 *
 * Note that the caller of a COM method uses a non-S_OK result code to
 * decide if the method has returned an error (negative codes) or a warning
 * (positive non-zero codes) and will query extended error info only in
 * these two cases. However, since multi-error mode implies that the method
 * doesn't return control return to the caller immediately after the first
 * error or warning but continues its execution, the functionality provided
 * by the base com::FWResult class becomes very useful because it allows to
 * preserve the error or the warning result code even if it is later assigned
 * a S_OK value multiple times. See com::FWResult for details.
 *
 * Here is the typical usage pattern:
 *  <code>

    HRESULT Bar::method()
    {
        // assume multi-errors are turned off here...

        if (something)
        {
            // Turn on multi-error mode and make sure severity is preserved
            MultiResult rc = foo->method1();

            // return on fatal error, but continue on warning or on success
            CheckComRCReturnRC (rc);

            rc = foo->method2();
            // no matter what result, stack it and continue

            // ...

            // return the last worst result code (it will be preserved even if
            // foo->method2() returns S_OK.
            return rc;
        }

        // multi-errors are turned off here again...

        return S_OK;
    }

 *  </code>
 *
 * @note This class is intended to be instantiated on the stack, therefore
 *       You cannot create them using new(). Although it is possible to copy
 *       instances of MultiResult or return them by value, please never do
 *       that as it is breaks the class semantics (and will assert);
 */
class MultiResult : public FWResult
{
public:

    /**
     * @copydoc FWResult::FWResult().
     */
    MultiResult (HRESULT aRC = E_FAIL) : FWResult (aRC) { incCounter(); }

    MultiResult (const MultiResult &aThat) : FWResult (aThat)
    {
        /* We need this copy constructor only for GCC that wants to have
         * it in case of expressions like |MultiResult rc = E_FAIL;|. But
         * we assert since the optimizer should actually avoid the
         * temporary and call the other constructor directly instead. */
        AssertFailed();
    }

    ~MultiResult() { decCounter(); }

    MultiResult &operator= (HRESULT aRC)
    {
        FWResult::operator= (aRC);
        return *this;
    }

    MultiResult &operator= (const MultiResult & /* aThat */)
    {
        /* We need this copy constructor only for GCC that wants to have
         * it in case of expressions like |MultiResult rc = E_FAIL;|. But
         * we assert since the optimizer should actually avoid the
         * temporary and call the other constructor directly instead. */
        AssertFailed();
        return *this;
    }

    /**
     * Returns true if multi-mode is enabled for the current thread (i.e. at
     * least one MultiResult instance exists on the stack somewhere).
     * @return
     */
    static bool isMultiEnabled();

private:

    DECLARE_CLS_NEW_DELETE_NOOP(MultiResult)

    static void incCounter();
    static void decCounter();

    static RTTLS sCounter;

    friend class MultiResultRef;
};

/**
 * The MultiResultRef class is equivalent to MultiResult except that it takes
 * a reference to the existing HRESULT variable instead of maintaining its own
 * one.
 */
class MultiResultRef
{
public:

    MultiResultRef (HRESULT &aRC) : mRC (aRC) { MultiResult::incCounter(); }

    ~MultiResultRef() { MultiResult::decCounter(); }

    MultiResultRef &operator= (HRESULT aRC)
    {
        /* Copied from FWResult */
        if ((FAILED (aRC) && !FAILED (mRC)) ||
            (mRC == S_OK && aRC != S_OK))
            mRC = aRC;

        return *this;
    }

    operator HRESULT() const { return mRC; }

    HRESULT *operator&() { return &mRC; }

private:

    DECLARE_CLS_NEW_DELETE_NOOP (MultiResultRef)

    HRESULT &mRC;
};


} /* namespace com */

#endif /* ___VBox_com_MultiResult_h */