summaryrefslogtreecommitdiff
path: root/mfbt/Scoped.h
blob: 677a1a37978b6c33f6b107b67f18a0fa66a9c170 (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
263
264
265
266
267
268
269
270
271
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/* A number of structures to simplify scope-based RAII management. */

#ifndef mozilla_Scoped_h_
#define mozilla_Scoped_h_

/*
 * Resource Acquisition Is Initialization is a programming idiom used
 * to write robust code that is able to deallocate resources properly,
 * even in presence of execution errors or exceptions that need to be
 * propagated.  The Scoped* classes defined in this header perform the
 * deallocation of the resource they hold once program execution
 * reaches the end of the scope for which they have been defined.
 *
 * This header provides the following RAII classes:
 *
 * - |ScopedFreePtr| - a container for a pointer, that automatically calls
 *   |free()| at the end of the scope;
 * - |ScopedDeletePtr| - a container for a pointer, that automatically calls
 *   |delete| at the end of the scope;
 * - |ScopedDeleteArray| - a container for a pointer to an array, that
 *   automatically calls |delete[]| at the end of the scope.
 *
 * The general scenario for each of the RAII classes is the following:
 *
 * ScopedClass foo(create_value());
 * // ... In this scope, |foo| is defined. Use |foo.get()| or |foo.rwget()|
 *        to access the value.
 * // ... In case of |return| or |throw|, |foo| is deallocated automatically.
 * // ... If |foo| needs to be returned or stored, use |foo.forget()|
 *
 * Note that the RAII classes defined in this header do _not_ perform any form
 * of reference-counting or garbage-collection. These classes have exactly two
 * behaviors:
 *
 * - if |forget()| has not been called, the resource is always deallocated at
 *   the end of the scope;
 * - if |forget()| has been called, any control on the resource is unbound
 *   and the resource is not deallocated by the class.
 *
 * Extension:
 *
 * In addition, this header provides class |Scoped| and macros |SCOPED_TEMPLATE|
 * and |MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE|  to simplify the definition
 * of RAII classes for other scenarios. These macros have been used to
 * automatically close file descriptors/file handles when reaching the end of
 * the scope, graphics contexts, etc.
 */

#include "mozilla/Attributes.h"
#include "mozilla/GuardObjects.h"

namespace mozilla {

/*
 * Scoped is a helper to create RAII wrappers
 * Type argument |Traits| is expected to have the following structure:
 *
 *   struct Traits {
 *     // Define the type of the value stored in the wrapper
 *     typedef value_type type;
 *     // Returns the value corresponding to the uninitialized or freed state
 *     const static type empty();
 *     // Release resources corresponding to the wrapped value
 *     // This function is responsible for not releasing an |empty| value
 *     const static void release(type);
 *   }
 */
template<typename Traits>
class Scoped
{
  public:
    typedef typename Traits::type Resource;

    explicit Scoped(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
      : value(Traits::empty())
    {
      MOZ_GUARD_OBJECT_NOTIFIER_INIT;
    }
    explicit Scoped(const Resource& v
                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
      : value(v)
    {
      MOZ_GUARD_OBJECT_NOTIFIER_INIT;
    }
    ~Scoped() {
      Traits::release(value);
    }

    // Constant getter
    operator const Resource&() const { return value; }
    const Resource& operator->() const { return value; }
    const Resource& get() const { return value; }
    // Non-constant getter.
    Resource& rwget() { return value; }

    /*
     * Forget the resource.
     *
     * Once |forget| has been called, the |Scoped| is neutralized, i.e. it will
     * have no effect at destruction (unless it is reset to another resource by
     * |operator=|).
     *
     * @return The original resource.
     */
    Resource forget() {
      Resource tmp = value;
      value = Traits::empty();
      return tmp;
    }

    /*
     * Perform immediate clean-up of this |Scoped|.
     *
     * If this |Scoped| is currently empty, this method has no effect.
     */
    void dispose() {
      Traits::release(value);
      value = Traits::empty();
    }

    bool operator==(const Resource& other) const {
      return value == other;
    }

    /*
     * Replace the resource with another resource.
     *
     * Calling |operator=| has the side-effect of triggering clean-up. If you do
     * not want to trigger clean-up, you should first invoke |forget|.
     *
     * @return this
     */
    Scoped<Traits>& operator=(const Resource& other) {
      return reset(other);
    }
    Scoped<Traits>& reset(const Resource& other) {
      Traits::release(value);
      value = other;
      return *this;
    }

  private:
    explicit Scoped(const Scoped<Traits>& value) MOZ_DELETE;
    Scoped<Traits>& operator=(const Scoped<Traits>& value) MOZ_DELETE;

  private:
    Resource value;
    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};

/*
 * SCOPED_TEMPLATE defines a templated class derived from Scoped
 * This allows to implement templates such as ScopedFreePtr.
 *
 * @param name The name of the class to define.
 * @param Traits A struct implementing clean-up. See the implementations
 * for more details.
 */
#define SCOPED_TEMPLATE(name, Traits)                          \
template<typename Type>                                        \
struct name : public mozilla::Scoped<Traits<Type> >            \
{                                                              \
    typedef mozilla::Scoped<Traits<Type> > Super;              \
    typedef typename Super::Resource Resource;                 \
    name& operator=(Resource ptr) {                            \
      Super::operator=(ptr);                                   \
      return *this;                                            \
    }                                                          \
    explicit name(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)        \
      : Super(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT)  \
    {}                                                         \
    explicit name(Resource ptr                                 \
                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM)             \
      : Super(ptr MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)   \
    {}                                                         \
  private:                                                     \
    explicit name(name& source) MOZ_DELETE;                    \
    name& operator=(name& source) MOZ_DELETE;                  \
};

/*
 * ScopedFreePtr is a RAII wrapper for pointers that need to be free()d.
 *
 *   struct S { ... };
 *   ScopedFreePtr<S> foo = malloc(sizeof(S));
 *   ScopedFreePtr<char> bar = strdup(str);
 */
template<typename T>
struct ScopedFreePtrTraits
{
    typedef T* type;
    static T* empty() { return NULL; }
    static void release(T* ptr) { free(ptr); }
};
SCOPED_TEMPLATE(ScopedFreePtr, ScopedFreePtrTraits)

/*
 * ScopedDeletePtr is a RAII wrapper for pointers that need to be deleted.
 *
 *   struct S { ... };
 *   ScopedDeletePtr<S> foo = new S();
 */
template<typename T>
struct ScopedDeletePtrTraits : public ScopedFreePtrTraits<T>
{
    static void release(T* ptr) { delete ptr; }
};
SCOPED_TEMPLATE(ScopedDeletePtr, ScopedDeletePtrTraits)

/*
 * ScopedDeleteArray is a RAII wrapper for pointers that need to be delete[]ed.
 *
 *   struct S { ... };
 *   ScopedDeleteArray<S> foo = new S[42];
 */
template<typename T>
struct ScopedDeleteArrayTraits : public ScopedFreePtrTraits<T>
{
    static void release(T* ptr) { delete [] ptr; }
};
SCOPED_TEMPLATE(ScopedDeleteArray, ScopedDeleteArrayTraits)

/*
 * MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE makes it easy to create scoped
 * pointers for types with custom deleters; just overload
 * TypeSpecificDelete(T*) in the same namespace as T to call the deleter for
 * type T.
 *
 * @param name The name of the class to define.
 * @param Type A struct implementing clean-up. See the implementations
 * for more details.
 * *param Deleter The function that is used to delete/destroy/free a
 *        non-null value of Type*.
 *
 * Example:
 *
 *   MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, \
 *                                             PR_Close)
 *   ...
 *   {
 *       ScopedPRFileDesc file(PR_OpenFile(...));
 *       ...
 *   } // file is closed with PR_Close here
 */
#define MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(name, Type, Deleter) \
template <> inline void TypeSpecificDelete(Type * value) { Deleter(value); } \
typedef ::mozilla::TypeSpecificScopedPointer<Type> name;

template <typename T> void TypeSpecificDelete(T * value);

template <typename T>
struct TypeSpecificScopedPointerTraits
{
    typedef T* type;
    const static type empty() { return NULL; }
    const static void release(type value)
    {
      if (value)
        TypeSpecificDelete(value);
    }
};

SCOPED_TEMPLATE(TypeSpecificScopedPointer, TypeSpecificScopedPointerTraits)

} /* namespace mozilla */

#endif // mozilla_Scoped_h_