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
|
#pragma once
#include "UThreadData.h"
// Note: The implementation is in UThreadData.cpp.
namespace os {
/**
* This is a handle to a specific UThread. Currently, not many operations
* is supported on another UThread than the current. Therefore, the backing
* UThread is not kept after the UThread has terminated.
*/
class UThread {
public:
/**
* Description of how to handle the result from a function call on another
* thread. When the function completes, 'done' is called with its result.
* If an exception is thrown, 'error' is called with the exception instead.
* 'data' may be used to provide custom data to the callbacks.
*/
template <class R, class Param>
struct Result {
Param *data;
void (*done)(Param *, R);
void (*error)(Param *, const Exception &);
};
// Specific for void.
template <class Param>
struct Result<void, Param> {
Param *data;
void (*done)(Param *);
void (*error)(Param *, const Exception &);
};
// Copy.
UThread(const UThread &o) : data(o.data) {
if (data)
data->addRef();
}
// Assign.
UThread &operator =(const UThread &o) {
if (this != &o) {
if (data)
data->release();
data = o.data;
if (data)
data->addRef();
}
return *this;
}
// Destroy.
~UThread() {
if (data)
data->release();
}
#ifdef USE_MOVE
// Move.
UThread(UThread &&o) : data(o.data) {
o.data = null;
}
UThread &operator =(UThread &&o) {
std::swap(data, o.data);
return *this;
}
#endif
// Same UThread?
inline bool operator ==(const UThread &o) const { return data == o.data; }
inline bool operator !=(const UThread &o) const { return data != o.data; }
// Get an unique identifier for this thread.
inline uintptr_t id() const { return (uintptr_t)data; }
// Yield to another UThread. Returns sometime in the future. Returns 'true' if other
// threads were run between the call and the return.
static bool leave();
// See if there are any UThreads that are currently sleeping, and may wake in the future.
// This can be used together with "leave" to detect if we need to keep "polling" for
// sleeping threads from custom thread-wait logic in certain cases (e.g. when it cannot use
// the standard "wait" function).
static bool anySleeping();
// Yield for a specific amount of time.
static void sleep(nat ms);
// Any more UThreads to run here?
static bool any();
// Get the currently running UThread.
static UThread current();
// Get the thread data. Mainly for internal use.
inline UThreadData *threadData() { return data; }
// Value representing no thread.
static const UThread invalid;
/**
* Low-level spawn functions. See 'spawn' below for explanations.
*/
// Spawn a 'void' function.
static UThread spawnRaw(const void *fn, bool memberFn, void *firstParam,
const FnCallRaw &call, const Thread *on = null);
// Spawn a function, capturing the result in a future.
static UThread spawnRaw(const void *fn, bool memberFn, void *firstParam,
const FnCallRaw &call, FutureBase &result,
void *target, const Thread *on = null);
/**
* Spawn a new UThread on the currently running thread, or another thread. There are a few
* variants of spawn here, all of them take some kind of function pointer, optionally with
* parameters to run, followed by a reference to the OS thread (Thread *) to run on. If this
* is left out, or set to null, the thread of the caller is used.
*
* All of these return as soon as the new UThread is set up, they do not pre-empt the
* currently running thread. Note, however, that any parameters are copied before the call
* returns, so there is no need to worry about variables used as parameters going out of
* scope.
*/
// Spawn using a Fn<void, void>.
static UThread spawn(const util::Fn<void, void> &fn, const Thread *on = null);
// Spawn using a FnCall object.
template <int P>
static UThread spawn(const void *fn, bool memberFn, const FnCall<void, P> &call, const Thread *on = null) {
return spawnRaw(fn, memberFn, null, call, on);
}
// Spawn using a FnCall object, providing the result in a Future<T>.
template <class R, int P>
static UThread spawn(const void *fn, bool memberFn, const FnCall<R, P> &call,
Future<R> &result, const Thread *on = null) {
return spawnRaw(fn, memberFn, null, call, result.impl(), result.data(), on);
}
// Execute a detour function on this uthread. Assumes that this thread is currently not
// running and belongs to the same thread as the caller of this function. The detour will be
// executed synchronously, ie. the current thread will be suspended until the detour is
// completed.
// This function is intended to provide stack traces for suspended threads. As such, the
// stack during the detour will look like the suspended thread called the function in the
// detour.
// Returns 'false' if unable to execute the detour, usually since the thread is currently
// being executed.
bool detour(const util::Fn<void, void> &fn);
private:
friend class UThreadState;
// Create
UThread(UThreadData *data);
// Referenced data.
UThreadData *data;
// Insert an UThread.
static UThread insert(UThreadData *data, ThreadData *on);
};
}
// Don't ask...
#include "Sync.h"
|