1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mfbt/Scoped.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,305 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* A number of structures to simplify scope-based RAII management. */ 1.11 + 1.12 +#ifndef mozilla_Scoped_h 1.13 +#define mozilla_Scoped_h 1.14 + 1.15 +/* 1.16 + * Resource Acquisition Is Initialization is a programming idiom used 1.17 + * to write robust code that is able to deallocate resources properly, 1.18 + * even in presence of execution errors or exceptions that need to be 1.19 + * propagated. The Scoped* classes defined in this header perform the 1.20 + * deallocation of the resource they hold once program execution 1.21 + * reaches the end of the scope for which they have been defined. 1.22 + * 1.23 + * This header provides the following RAII classes: 1.24 + * 1.25 + * - |ScopedFreePtr| - a container for a pointer, that automatically calls 1.26 + * |free()| at the end of the scope; 1.27 + * - |ScopedDeletePtr| - a container for a pointer, that automatically calls 1.28 + * |delete| at the end of the scope; 1.29 + * - |ScopedDeleteArray| - a container for a pointer to an array, that 1.30 + * automatically calls |delete[]| at the end of the scope. 1.31 + * 1.32 + * The general scenario for each of the RAII classes is the following: 1.33 + * 1.34 + * ScopedClass foo(create_value()); 1.35 + * // ... In this scope, |foo| is defined. Use |foo.get()| or |foo.rwget()| 1.36 + * to access the value. 1.37 + * // ... In case of |return| or |throw|, |foo| is deallocated automatically. 1.38 + * // ... If |foo| needs to be returned or stored, use |foo.forget()| 1.39 + * 1.40 + * Note that the RAII classes defined in this header do _not_ perform any form 1.41 + * of reference-counting or garbage-collection. These classes have exactly two 1.42 + * behaviors: 1.43 + * 1.44 + * - if |forget()| has not been called, the resource is always deallocated at 1.45 + * the end of the scope; 1.46 + * - if |forget()| has been called, any control on the resource is unbound 1.47 + * and the resource is not deallocated by the class. 1.48 + * 1.49 + * Extension: 1.50 + * 1.51 + * In addition, this header provides class |Scoped| and macros |SCOPED_TEMPLATE| 1.52 + * and |MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE| to simplify the definition 1.53 + * of RAII classes for other scenarios. These macros have been used to 1.54 + * automatically close file descriptors/file handles when reaching the end of 1.55 + * the scope, graphics contexts, etc. 1.56 + */ 1.57 + 1.58 +#include "mozilla/Assertions.h" 1.59 +#include "mozilla/Attributes.h" 1.60 +#include "mozilla/GuardObjects.h" 1.61 +#include "mozilla/Move.h" 1.62 +#include "mozilla/NullPtr.h" 1.63 + 1.64 +namespace mozilla { 1.65 + 1.66 +/* 1.67 + * Scoped is a helper to create RAII wrappers 1.68 + * Type argument |Traits| is expected to have the following structure: 1.69 + * 1.70 + * struct Traits { 1.71 + * // Define the type of the value stored in the wrapper 1.72 + * typedef value_type type; 1.73 + * // Returns the value corresponding to the uninitialized or freed state 1.74 + * const static type empty(); 1.75 + * // Release resources corresponding to the wrapped value 1.76 + * // This function is responsible for not releasing an |empty| value 1.77 + * const static void release(type); 1.78 + * } 1.79 + */ 1.80 +template<typename Traits> 1.81 +class Scoped 1.82 +{ 1.83 + public: 1.84 + typedef typename Traits::type Resource; 1.85 + 1.86 + explicit Scoped(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) 1.87 + : value(Traits::empty()) 1.88 + { 1.89 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.90 + } 1.91 + 1.92 + explicit Scoped(const Resource& v 1.93 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) 1.94 + : value(v) 1.95 + { 1.96 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.97 + } 1.98 + 1.99 + /* Move constructor. */ 1.100 + explicit Scoped(Scoped&& v 1.101 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) 1.102 + : value(Move(v.value)) 1.103 + { 1.104 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.105 + v.value = Traits::empty(); 1.106 + } 1.107 + 1.108 + ~Scoped() { 1.109 + Traits::release(value); 1.110 + } 1.111 + 1.112 + // Constant getter 1.113 + operator const Resource&() const { return value; } 1.114 + const Resource& operator->() const { return value; } 1.115 + const Resource& get() const { return value; } 1.116 + // Non-constant getter. 1.117 + Resource& rwget() { return value; } 1.118 + 1.119 + /* 1.120 + * Forget the resource. 1.121 + * 1.122 + * Once |forget| has been called, the |Scoped| is neutralized, i.e. it will 1.123 + * have no effect at destruction (unless it is reset to another resource by 1.124 + * |operator=|). 1.125 + * 1.126 + * @return The original resource. 1.127 + */ 1.128 + Resource forget() { 1.129 + Resource tmp = value; 1.130 + value = Traits::empty(); 1.131 + return tmp; 1.132 + } 1.133 + 1.134 + /* 1.135 + * Perform immediate clean-up of this |Scoped|. 1.136 + * 1.137 + * If this |Scoped| is currently empty, this method has no effect. 1.138 + */ 1.139 + void dispose() { 1.140 + Traits::release(value); 1.141 + value = Traits::empty(); 1.142 + } 1.143 + 1.144 + bool operator==(const Resource& other) const { 1.145 + return value == other; 1.146 + } 1.147 + 1.148 + /* 1.149 + * Replace the resource with another resource. 1.150 + * 1.151 + * Calling |operator=| has the side-effect of triggering clean-up. If you do 1.152 + * not want to trigger clean-up, you should first invoke |forget|. 1.153 + * 1.154 + * @return this 1.155 + */ 1.156 + Scoped& operator=(const Resource& other) { 1.157 + return reset(other); 1.158 + } 1.159 + Scoped& reset(const Resource& other) { 1.160 + Traits::release(value); 1.161 + value = other; 1.162 + return *this; 1.163 + } 1.164 + 1.165 + /* Move assignment operator. */ 1.166 + Scoped& operator=(Scoped&& rhs) { 1.167 + MOZ_ASSERT(&rhs != this, "self-move-assignment not allowed"); 1.168 + this->~Scoped(); 1.169 + new(this) Scoped(Move(rhs)); 1.170 + return *this; 1.171 + } 1.172 + 1.173 + private: 1.174 + explicit Scoped(const Scoped& value) MOZ_DELETE; 1.175 + Scoped& operator=(const Scoped& value) MOZ_DELETE; 1.176 + 1.177 + private: 1.178 + Resource value; 1.179 + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 1.180 +}; 1.181 + 1.182 +/* 1.183 + * SCOPED_TEMPLATE defines a templated class derived from Scoped 1.184 + * This allows to implement templates such as ScopedFreePtr. 1.185 + * 1.186 + * @param name The name of the class to define. 1.187 + * @param Traits A struct implementing clean-up. See the implementations 1.188 + * for more details. 1.189 + */ 1.190 +#define SCOPED_TEMPLATE(name, Traits) \ 1.191 +template<typename Type> \ 1.192 +struct name : public mozilla::Scoped<Traits<Type> > \ 1.193 +{ \ 1.194 + typedef mozilla::Scoped<Traits<Type> > Super; \ 1.195 + typedef typename Super::Resource Resource; \ 1.196 + name& operator=(Resource rhs) { \ 1.197 + Super::operator=(rhs); \ 1.198 + return *this; \ 1.199 + } \ 1.200 + name& operator=(name&& rhs) { \ 1.201 + Super::operator=(Move(rhs)); \ 1.202 + return *this; \ 1.203 + } \ 1.204 + explicit name(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) \ 1.205 + : Super(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT) \ 1.206 + {} \ 1.207 + explicit name(Resource rhs \ 1.208 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) \ 1.209 + : Super(rhs \ 1.210 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) \ 1.211 + {} \ 1.212 + explicit name(name&& rhs \ 1.213 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) \ 1.214 + : Super(Move(rhs) \ 1.215 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) \ 1.216 + {} \ 1.217 + private: \ 1.218 + explicit name(name&) MOZ_DELETE; \ 1.219 + name& operator=(name&) MOZ_DELETE; \ 1.220 +}; 1.221 + 1.222 +/* 1.223 + * ScopedFreePtr is a RAII wrapper for pointers that need to be free()d. 1.224 + * 1.225 + * struct S { ... }; 1.226 + * ScopedFreePtr<S> foo = malloc(sizeof(S)); 1.227 + * ScopedFreePtr<char> bar = strdup(str); 1.228 + */ 1.229 +template<typename T> 1.230 +struct ScopedFreePtrTraits 1.231 +{ 1.232 + typedef T* type; 1.233 + static T* empty() { return nullptr; } 1.234 + static void release(T* ptr) { free(ptr); } 1.235 +}; 1.236 +SCOPED_TEMPLATE(ScopedFreePtr, ScopedFreePtrTraits) 1.237 + 1.238 +/* 1.239 + * ScopedDeletePtr is a RAII wrapper for pointers that need to be deleted. 1.240 + * 1.241 + * struct S { ... }; 1.242 + * ScopedDeletePtr<S> foo = new S(); 1.243 + */ 1.244 +template<typename T> 1.245 +struct ScopedDeletePtrTraits : public ScopedFreePtrTraits<T> 1.246 +{ 1.247 + static void release(T* ptr) { delete ptr; } 1.248 +}; 1.249 +SCOPED_TEMPLATE(ScopedDeletePtr, ScopedDeletePtrTraits) 1.250 + 1.251 +/* 1.252 + * ScopedDeleteArray is a RAII wrapper for pointers that need to be delete[]ed. 1.253 + * 1.254 + * struct S { ... }; 1.255 + * ScopedDeleteArray<S> foo = new S[42]; 1.256 + */ 1.257 +template<typename T> 1.258 +struct ScopedDeleteArrayTraits : public ScopedFreePtrTraits<T> 1.259 +{ 1.260 + static void release(T* ptr) { delete [] ptr; } 1.261 +}; 1.262 +SCOPED_TEMPLATE(ScopedDeleteArray, ScopedDeleteArrayTraits) 1.263 + 1.264 +/* 1.265 + * MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE makes it easy to create scoped 1.266 + * pointers for types with custom deleters; just overload 1.267 + * TypeSpecificDelete(T*) in the same namespace as T to call the deleter for 1.268 + * type T. 1.269 + * 1.270 + * @param name The name of the class to define. 1.271 + * @param Type A struct implementing clean-up. See the implementations 1.272 + * for more details. 1.273 + * *param Deleter The function that is used to delete/destroy/free a 1.274 + * non-null value of Type*. 1.275 + * 1.276 + * Example: 1.277 + * 1.278 + * MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, \ 1.279 + * PR_Close) 1.280 + * ... 1.281 + * { 1.282 + * ScopedPRFileDesc file(PR_OpenFile(...)); 1.283 + * ... 1.284 + * } // file is closed with PR_Close here 1.285 + */ 1.286 +#define MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(name, Type, Deleter) \ 1.287 +template <> inline void TypeSpecificDelete(Type * value) { Deleter(value); } \ 1.288 +typedef ::mozilla::TypeSpecificScopedPointer<Type> name; 1.289 + 1.290 +template <typename T> void TypeSpecificDelete(T * value); 1.291 + 1.292 +template <typename T> 1.293 +struct TypeSpecificScopedPointerTraits 1.294 +{ 1.295 + typedef T* type; 1.296 + const static type empty() { return nullptr; } 1.297 + const static void release(type value) 1.298 + { 1.299 + if (value) 1.300 + TypeSpecificDelete(value); 1.301 + } 1.302 +}; 1.303 + 1.304 +SCOPED_TEMPLATE(TypeSpecificScopedPointer, TypeSpecificScopedPointerTraits) 1.305 + 1.306 +} /* namespace mozilla */ 1.307 + 1.308 +#endif /* mozilla_Scoped_h */