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 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
|
#pragma once
#include "Utils/TypeInfo.h"
#include "Utils/Lock.h"
#include "PtrThrowable.h"
#include "InlineList.h"
// In Visual Studio 2008, we do not have exception_ptr, so we implement our own
// solution in that case!
#if defined(VISUAL_STUDIO) && VISUAL_STUDIO <= 2008
#define CUSTOM_EXCEPTION_PTR
#else
#include <stdexcept>
#endif
namespace os {
class UThreadData;
#ifdef CUSTOM_EXCEPTION_PTR
struct CppExceptionType;
#endif
/**
* Mode for futures:
*/
struct FutureMode {
// There are two modes in which the Future can be used in respect to how they interact with
// other UThreads on the current OS thread.
enum Mode {
// The normal mode, when this future is waiting, any other UThread may be scheduled.
normal,
// The exclusive mode. This means that when this future is waiting for a result, no
// other UThreads are allowed to run on the current OS thread. The exception is other
// threads that are spawned to post a result to an exclusive future, or their
// descendants. This is used in the lazy compilation to not break the threading
// guarantees provided by Storm (i.e. function calls do not cause a thread switch by
// themselves).
exclusive,
};
};
/**
* Future that stores the result in some unrelated memory. This class is designed to be possible
* to use with minimal type information and to be easy to use from machine code or general
* non-templated C++ code. Therefore it is not very type-safe at all. For a more type-safe and
* user-friendly version, see the Future<> class below.
*/
class FutureBase : NoCopy {
public:
// Create the future. Indicate a priority level.
FutureBase(FutureMode::Mode mode = FutureMode::normal);
// Detect unhandled exceptions and print them.
~FutureBase();
// Wait for the result. This function will either return normally, indicating the result is
// written to 'target', or throw an exception posted.
void result();
// Same as above, but provides an opportunity to modify any pointer-based exception before
// it is thrown.
typedef PtrThrowable *(*InterceptFn)(PtrThrowable *original, void *env);
void result(InterceptFn fn, void *env);
// Detach this exception, ie. don't complain about uncaught errors.
void detach();
// Tell the waiting thread we have posted a result.
void posted();
// Post an error. This function must be called from within a throw-catch block. Otherwise
// the runtime crashes.
void error();
// Has any result been posted?
bool dataPosted();
// Any result (either data or error) posted?
bool anyPosted();
// Check if the future is in exclusive mode.
inline bool exclusive() const { return mode == FutureMode::exclusive; }
// Pointer-based exceptions are stored here so that they can be garbage collected properly.
PtrThrowable *ptrException;
private:
// Throw the error captured earlier.
void throwError();
// Throw the error captured earlier, knowing it is a pointer.
void throwPtrError(InterceptFn fn, void *env);
// Save the current exception.
void saveError();
// Save the current exception, knowing it is a pointer.
void savePtrError(const PtrThrowable *exception);
// Perform any cleanup needed.
void cleanError();
#ifdef CUSTOM_EXCEPTION_PTR
class ExceptionPtr {
public:
ExceptionPtr();
~ExceptionPtr();
void *data;
const CppExceptionType *type;
// Only used on 64-bit systems.
HINSTANCE instance;
// Rethrow this exception.
void rethrow();
void rethrowPtr(PtrThrowable *&ptr);
// Clear the data here.
void clear();
// Copy from an exception.
void set(void *from, const CppExceptionType *type, HINSTANCE instance);
// Copy a pointer from an exception.
void setPtr(void *from, const CppExceptionType *type, HINSTANCE instance, PtrThrowable *&store);
};
// The exception.
ExceptionPtr exceptionData;
// Filter expression function.
int filter(EXCEPTION_POINTERS *ptrs, bool pointer);
#elif defined(POSIX)
// Exception data. Either an std::exception ptr (if it was a C++ exception), or a
// std::type_info * if it was a pointer exception.
size_t exceptionData[sizeof(std::exception_ptr) / sizeof(size_t)];
#else
int filter(EXCEPTION_POINTERS *ptrs, InterceptFn fn, void *env);
// Regular implementation, we use filter functions to modify the data.
std::exception_ptr exceptionData;
#endif
// Any error?
bool hasError() const {
nat r = atomicRead(resultPosted);
return r == resultError || r == resultErrorPtr;
}
// What kind of result was posted?
enum {
// No result.
resultEmpty,
// A regular value.
resultValue,
// A generic C++ exception.
resultError,
// A pointer to PtrThrowable.
resultErrorPtr,
};
// Anything posted? Any of 'result*'
nat resultPosted;
// Did we read the result?
enum {
// Not read.
readNone,
// Read at least once.
readOnce,
// Detached. i.e. will not be read.
readDetached,
};
// Anything read?
nat resultRead;
// Mode.
FutureMode::Mode mode;
// Warn about errors from detached threads.
void warnDetachedError();
protected:
// Notify/wait operations. These were previously in a separate subclass to allow swapping
// which semaphore to use. Now, we implement our own event-like structure to allow custom
// waiting behavior and avoid circular dependencies (sema depends on UThread, UThread
// depends on us). We still need to let other classes intercept these operations though.
virtual void notify();
virtual void wait();
private:
// Threads waiting for this future.
InlineList<UThreadData> waitingThreads;
// Has 'notify' been called?
bool notified;
// Lock for 'waitingThreads' and 'notified'.
util::Lock lock;
};
/**
* A real C++ friendly implementation of the future. Defaults to use the
* semaphore with support for background threads. This class does not
* inherit from the FutureBase<> class to provide a cleaner interface
* to the user. The underlying FutureBase<> object may still be queried
* if neccessary.
* This class assumes that you will call 'result' at least once.
*/
template <class T>
class Future : NoCopy {
public:
// Create, optionally specify a mode.
Future() {}
Future(FutureMode::Mode mode) : future(mode) {}
// Destroy.
~Future() {
if (future.dataPosted()) {
void *v = value;
((T *)v)->~T();
}
}
// Get the result or throw the error, when the thread is ready.
T result() {
future.result();
void *v = value;
return *(T *)v;
}
// Get the result, specify a intercept function.
T result(FutureBase::InterceptFn fn, void *env) {
future.result(fn, env);
void *v = value;
return *(T *)v;
}
// Post the result.
void post(const T &result) {
new (value) T(result);
future.posted();
}
// Post an error. Only call from a catch-block.
void error() {
future.error();
}
// Get the underlying Future object.
FutureBase &impl() {
return future;
}
// Get our data.
void *data() {
return value;
}
private:
// Underlying object.
FutureBase future;
// Result storage. Note that we do not want to initialize it before
// 'future' tries to return something into this.
byte value[sizeof(T)];
};
/**
* Special case for void values (exceptions and syncronization may still be interesting!).
*/
template <>
class Future<void> : NoCopy {
public:
// Create, optionally specify a mode.
Future() {}
Future(FutureMode::Mode mode) : future(mode) {}
// Destroy.
~Future() {}
// Get the result or throw the error, when the thread is ready.
void result() {
future.result();
}
// Get the result, specify a intercept function.
void result(FutureBase::InterceptFn fn, void *env) {
future.result(fn, env);
}
// Post the result.
void post() {
future.posted();
}
// Post an error. Only call from a catch-block.
void error() {
future.error();
}
// Get the underlying Future object.
FutureBase &impl() {
return future;
}
// Get our data.
void *data() {
return null;
}
private:
// Underlying object.
FutureBase future;
};
}
|