1 //===--- CrashRecoveryContext.h - Crash Recovery ----------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #ifndef LLVM_SUPPORT_CRASHRECOVERYCONTEXT_H 11 #define LLVM_SUPPORT_CRASHRECOVERYCONTEXT_H 12 13 #include "llvm/ADT/STLExtras.h" 14 15 namespace llvm { 16 class CrashRecoveryContextCleanup; 17 18 /// Crash recovery helper object. 19 /// 20 /// This class implements support for running operations in a safe context so 21 /// that crashes (memory errors, stack overflow, assertion violations) can be 22 /// detected and control restored to the crashing thread. Crash detection is 23 /// purely "best effort", the exact set of failures which can be recovered from 24 /// is platform dependent. 25 /// 26 /// Clients make use of this code by first calling 27 /// CrashRecoveryContext::Enable(), and then executing unsafe operations via a 28 /// CrashRecoveryContext object. For example: 29 /// 30 /// \code 31 /// void actual_work(void *); 32 /// 33 /// void foo() { 34 /// CrashRecoveryContext CRC; 35 /// 36 /// if (!CRC.RunSafely(actual_work, 0)) { 37 /// ... a crash was detected, report error to user ... 38 /// } 39 /// 40 /// ... no crash was detected ... 41 /// } 42 /// \endcode 43 /// 44 /// To assist recovery the class allows specifying set of actions that will be 45 /// executed in any case, whether crash occurs or not. These actions may be used 46 /// to reclaim resources in the case of crash. 47 class CrashRecoveryContext { 48 void *Impl; 49 CrashRecoveryContextCleanup *head; 50 51 public: CrashRecoveryContext()52 CrashRecoveryContext() : Impl(nullptr), head(nullptr) {} 53 ~CrashRecoveryContext(); 54 55 /// Register cleanup handler, which is used when the recovery context is 56 /// finished. 57 /// The recovery context owns the handler. 58 void registerCleanup(CrashRecoveryContextCleanup *cleanup); 59 60 void unregisterCleanup(CrashRecoveryContextCleanup *cleanup); 61 62 /// Enable crash recovery. 63 static void Enable(); 64 65 /// Disable crash recovery. 66 static void Disable(); 67 68 /// Return the active context, if the code is currently executing in a 69 /// thread which is in a protected context. 70 static CrashRecoveryContext *GetCurrent(); 71 72 /// Return true if the current thread is recovering from a crash. 73 static bool isRecoveringFromCrash(); 74 75 /// Execute the provided callback function (with the given arguments) in 76 /// a protected context. 77 /// 78 /// \return True if the function completed successfully, and false if the 79 /// function crashed (or HandleCrash was called explicitly). Clients should 80 /// make as little assumptions as possible about the program state when 81 /// RunSafely has returned false. 82 bool RunSafely(function_ref<void()> Fn); RunSafely(void (* Fn)(void *),void * UserData)83 bool RunSafely(void (*Fn)(void*), void *UserData) { 84 return RunSafely([&]() { Fn(UserData); }); 85 } 86 87 /// Execute the provide callback function (with the given arguments) in 88 /// a protected context which is run in another thread (optionally with a 89 /// requested stack size). 90 /// 91 /// See RunSafely() and llvm_execute_on_thread(). 92 /// 93 /// On Darwin, if PRIO_DARWIN_BG is set on the calling thread, it will be 94 /// propagated to the new thread as well. 95 bool RunSafelyOnThread(function_ref<void()>, unsigned RequestedStackSize = 0); 96 bool RunSafelyOnThread(void (*Fn)(void*), void *UserData, 97 unsigned RequestedStackSize = 0) { 98 return RunSafelyOnThread([&]() { Fn(UserData); }, RequestedStackSize); 99 } 100 101 /// Explicitly trigger a crash recovery in the current process, and 102 /// return failure from RunSafely(). This function does not return. 103 void HandleCrash(); 104 }; 105 106 /// Abstract base class of cleanup handlers. 107 /// 108 /// Derived classes override method recoverResources, which makes actual work on 109 /// resource recovery. 110 /// 111 /// Cleanup handlers are stored in a double list, which is owned and managed by 112 /// a crash recovery context. 113 class CrashRecoveryContextCleanup { 114 protected: 115 CrashRecoveryContext *context; CrashRecoveryContextCleanup(CrashRecoveryContext * context)116 CrashRecoveryContextCleanup(CrashRecoveryContext *context) 117 : context(context), cleanupFired(false) {} 118 119 public: 120 bool cleanupFired; 121 122 virtual ~CrashRecoveryContextCleanup(); 123 virtual void recoverResources() = 0; 124 getContext()125 CrashRecoveryContext *getContext() const { 126 return context; 127 } 128 129 private: 130 friend class CrashRecoveryContext; 131 CrashRecoveryContextCleanup *prev, *next; 132 }; 133 134 /// Base class of cleanup handler that controls recovery of resources of the 135 /// given type. 136 /// 137 /// \tparam Derived Class that uses this class as a base. 138 /// \tparam T Type of controlled resource. 139 /// 140 /// This class serves as a base for its template parameter as implied by 141 /// Curiously Recurring Template Pattern. 142 /// 143 /// This class factors out creation of a cleanup handler. The latter requires 144 /// knowledge of the current recovery context, which is provided by this class. 145 template<typename Derived, typename T> 146 class CrashRecoveryContextCleanupBase : public CrashRecoveryContextCleanup { 147 protected: 148 T *resource; CrashRecoveryContextCleanupBase(CrashRecoveryContext * context,T * resource)149 CrashRecoveryContextCleanupBase(CrashRecoveryContext *context, T *resource) 150 : CrashRecoveryContextCleanup(context), resource(resource) {} 151 152 public: 153 /// Creates cleanup handler. 154 /// \param x Pointer to the resource recovered by this handler. 155 /// \return New handler or null if the method was called outside a recovery 156 /// context. create(T * x)157 static Derived *create(T *x) { 158 if (x) { 159 if (CrashRecoveryContext *context = CrashRecoveryContext::GetCurrent()) 160 return new Derived(context, x); 161 } 162 return nullptr; 163 } 164 }; 165 166 /// Cleanup handler that reclaims resource by calling destructor on it. 167 template <typename T> 168 class CrashRecoveryContextDestructorCleanup : public 169 CrashRecoveryContextCleanupBase<CrashRecoveryContextDestructorCleanup<T>, T> { 170 public: CrashRecoveryContextDestructorCleanup(CrashRecoveryContext * context,T * resource)171 CrashRecoveryContextDestructorCleanup(CrashRecoveryContext *context, 172 T *resource) 173 : CrashRecoveryContextCleanupBase< 174 CrashRecoveryContextDestructorCleanup<T>, T>(context, resource) {} 175 recoverResources()176 virtual void recoverResources() { 177 this->resource->~T(); 178 } 179 }; 180 181 /// Cleanup handler that reclaims resource by calling 'delete' on it. 182 template <typename T> 183 class CrashRecoveryContextDeleteCleanup : public 184 CrashRecoveryContextCleanupBase<CrashRecoveryContextDeleteCleanup<T>, T> { 185 public: CrashRecoveryContextDeleteCleanup(CrashRecoveryContext * context,T * resource)186 CrashRecoveryContextDeleteCleanup(CrashRecoveryContext *context, T *resource) 187 : CrashRecoveryContextCleanupBase< 188 CrashRecoveryContextDeleteCleanup<T>, T>(context, resource) {} 189 recoverResources()190 void recoverResources() override { delete this->resource; } 191 }; 192 193 /// Cleanup handler that reclaims resource by calling its method 'Release'. 194 template <typename T> 195 class CrashRecoveryContextReleaseRefCleanup : public 196 CrashRecoveryContextCleanupBase<CrashRecoveryContextReleaseRefCleanup<T>, T> { 197 public: CrashRecoveryContextReleaseRefCleanup(CrashRecoveryContext * context,T * resource)198 CrashRecoveryContextReleaseRefCleanup(CrashRecoveryContext *context, 199 T *resource) 200 : CrashRecoveryContextCleanupBase<CrashRecoveryContextReleaseRefCleanup<T>, 201 T>(context, resource) {} 202 recoverResources()203 void recoverResources() override { this->resource->Release(); } 204 }; 205 206 /// Helper class for managing resource cleanups. 207 /// 208 /// \tparam T Type of resource been reclaimed. 209 /// \tparam Cleanup Class that defines how the resource is reclaimed. 210 /// 211 /// Clients create objects of this type in the code executed in a crash recovery 212 /// context to ensure that the resource will be reclaimed even in the case of 213 /// crash. For example: 214 /// 215 /// \code 216 /// void actual_work(void *) { 217 /// ... 218 /// std::unique_ptr<Resource> R(new Resource()); 219 /// CrashRecoveryContextCleanupRegistrar D(R.get()); 220 /// ... 221 /// } 222 /// 223 /// void foo() { 224 /// CrashRecoveryContext CRC; 225 /// 226 /// if (!CRC.RunSafely(actual_work, 0)) { 227 /// ... a crash was detected, report error to user ... 228 /// } 229 /// \endcode 230 /// 231 /// If the code of `actual_work` in the example above does not crash, the 232 /// destructor of CrashRecoveryContextCleanupRegistrar removes cleanup code from 233 /// the current CrashRecoveryContext and the resource is reclaimed by the 234 /// destructor of std::unique_ptr. If crash happens, destructors are not called 235 /// and the resource is reclaimed by cleanup object registered in the recovery 236 /// context by the constructor of CrashRecoveryContextCleanupRegistrar. 237 template <typename T, typename Cleanup = CrashRecoveryContextDeleteCleanup<T> > 238 class CrashRecoveryContextCleanupRegistrar { 239 CrashRecoveryContextCleanup *cleanup; 240 241 public: CrashRecoveryContextCleanupRegistrar(T * x)242 CrashRecoveryContextCleanupRegistrar(T *x) 243 : cleanup(Cleanup::create(x)) { 244 if (cleanup) 245 cleanup->getContext()->registerCleanup(cleanup); 246 } 247 ~CrashRecoveryContextCleanupRegistrar()248 ~CrashRecoveryContextCleanupRegistrar() { unregister(); } 249 unregister()250 void unregister() { 251 if (cleanup && !cleanup->cleanupFired) 252 cleanup->getContext()->unregisterCleanup(cleanup); 253 cleanup = nullptr; 254 } 255 }; 256 } // end namespace llvm 257 258 #endif // LLVM_SUPPORT_CRASHRECOVERYCONTEXT_H 259