File: Future.h

package info (click to toggle)
storm-lang 0.7.5-2
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 52,100 kB
  • sloc: ansic: 261,471; cpp: 140,438; sh: 14,891; perl: 9,846; python: 2,525; lisp: 2,504; asm: 860; makefile: 678; pascal: 70; java: 52; xml: 37; awk: 12
file content (330 lines) | stat: -rw-r--r-- 8,196 bytes parent folder | download | duplicates (2)
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;
	};

}