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
|
#pragma once
#include "Object.h"
#include "Handle.h"
#include "OS/Future.h"
#include "OS/Sync.h"
#include "Core/GcTypeStore.h"
namespace storm {
STORM_PKG(core);
class Exception;
/**
* Future-object exposed to the runtime and built into function calls. By calling
* 'asyncThreadCall' on a function, you will get one of these objects. It also simplifies the
* interface of the Future in code since it supports copying of the future (references the same
* result) and also correctly handles cases where the result is not waited for.
*
* The future takes care of any copying of the objects.
*/
class FutureBase : public Object {
STORM_CLASS;
public:
// Create.
FutureBase(const Handle &type);
// Copy ctor.
FutureBase(const FutureBase &o);
// Destructor to keep the reference count of Data.
~FutureBase();
// Deep copy.
virtual void STORM_FN deepCopy(CloneEnv *env);
// Post a result.
void CODECALL postRaw(const void *value);
// Post an error (assuming we're inside a catch-block).
void error();
// Post an error.
void STORM_FN error(Exception *exception);
// Wait for the result. 'to' is empty memory where the value will be copied into.
void CODECALL resultRaw(void *to);
// Get the result as an exception if it was an exception, otherwise just return whenever we have a result.
void STORM_FN errorResult();
// Detach the future, ie. tell it we don't care about the result.
void STORM_FN detach();
// Get the underlying future object. Note: when you call this function,
// you are required to call either postRaw or error on the future object, otherwise
// we will leak resources!
os::FutureBase *rawFuture();
// Get the place where the result is to be stored.
void *rawResult() { return data->result->v; }
// Tell this future instance that it does not need to clone the result before returning it.
// This is set when we have a future that we know will be posted on the same Thread as the
// current thread. We will clear this flag as soon as "deepCopy" is called (which indicates
// that we crossed a Thread boundary).
void CODECALL setNoClone();
private:
// Custom extension of the 'FutureBase' object, so that we may get a notification when a
// result has been posted.
class FutureNotify : public os::FutureBase {
public:
// Ctor.
FutureNotify();
protected:
// Custom notify so that we can keep the Data struct alive long enough.
virtual void notify();
};
// Data shared between futures. Since we allow copies, we need to share one FutureBase
// object. This struct is allocated on the non-moving GC heap since it contains potentially
// sensitive constructs (like OS semaphores and locks).
//
// We use reference counting here to get slightly more prompt destruction of the data
// object. This is since the FutureBase object is allocated in a generational pool, which
// is collected more frequently, while the Data object is in a non-moving pool that is
// rarely collected. This way, we can at least free the OS resources earlier while making
// sure that we don't accidentally leak memory in certain situations (the finalizer on Data
// is a backup).
struct Data {
Data(const Handle &handle, GcArray<byte> *result);
~Data();
// The Future object we are using.
FutureNotify future;
// Handle.
const Handle *handle;
// Result. We use 'filled' to determine if anything is stored here.
GcArray<byte> *result;
// References.
nat refs;
// Release on receiving a result?
nat releaseOnResult;
// Add/release references.
void addRef();
void release();
// Called when the semaphore is notified.
static void resultPosted(FutureNotify *sema);
// Finalizer for this type.
static void finalize(void *object, os::Thread *thread);
// GC description for this type.
static const GcTypeStore<3> gcType;
};
// Pointer to the data.
UNKNOWN(PTR_GC) Data *data;
// Can we skip cloning the result?
Bool noClone;
};
// Declare the template.
STORM_TEMPLATE(Future, createFuture);
/**
* Specialized class, compatible with the template instantiations generated
* by the Storm runtime.
*/
template <class T>
class Future : public FutureBase {
STORM_SPECIAL;
public:
// Type lookup.
static Type *stormType(Engine &e) {
return runtime::cppTemplate(e, FutureId, 1, StormInfo<T>::id());
}
// Create.
Future() : FutureBase(StormInfo<T>::handle(engine())) {
runtime::setVTable(this);
}
// Copy.
Future(const Future<T> *o) : FutureBase(o) {
runtime::setVTable(this);
}
// Post result.
void post(T t) {
cloned(t, new (this) CloneEnv());
postRaw(&t);
}
// Get result.
T result() {
byte data[sizeof(T)];
resultRaw(data);
T copy = *(T *)data;
((T *)data)->~T();
return copy;
}
};
/**
* Special case for 'void'
*/
template <>
class Future<void> : public FutureBase {
STORM_SPECIAL;
public:
// Type lookup.
static Type *stormType(Engine &e) {
return runtime::cppTemplate(e, FutureId, 1, StormInfo<void>::id());
}
// Create.
Future() : FutureBase(StormInfo<void>::handle(engine())) {
runtime::setVTable(this);
}
// Copy.
Future(const Future<void> &o) : FutureBase(o) {
runtime::setVTable(this);
}
// Post.
void post() {
postRaw(null);
}
// Get result.
void result() {
resultRaw(null);
}
};
// Expose the "clone exception" helper for cases where we use the low-level os::Future directly
// in C++. This function makes sure to clone Storm exceptions before they are re-thrown from the
// Future. No env is needed for this function.
os::PtrThrowable *updateFutureExceptions(os::PtrThrowable *ptr, void *env);
}
|