diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-14 14:15:01 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-14 14:15:01 +0200 |
commit | 9589d1941671818f78d9894cfc9485054d62d122 (patch) | |
tree | c804998ba5cf89b75cb3d27052ee469fd4986595 /src/lane.h | |
parent | 1013970853e6acfd60591a89ae08cc40c64bee06 (diff) | |
download | lanes-9589d1941671818f78d9894cfc9485054d62d122.tar.gz lanes-9589d1941671818f78d9894cfc9485054d62d122.tar.bz2 lanes-9589d1941671818f78d9894cfc9485054d62d122.zip |
Move Lane implementation in a separate file
Diffstat (limited to 'src/lane.h')
-rw-r--r-- | src/lane.h | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/src/lane.h b/src/lane.h new file mode 100644 index 0000000..f28a402 --- /dev/null +++ b/src/lane.h | |||
@@ -0,0 +1,140 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include "cancel.h" | ||
4 | #include "uniquekey.h" | ||
5 | #include "universe.h" | ||
6 | |||
7 | #include <chrono> | ||
8 | #include <condition_variable> | ||
9 | #include <latch> | ||
10 | #include <stop_token> | ||
11 | #include <thread> | ||
12 | |||
13 | // ################################################################################################# | ||
14 | |||
15 | /* | ||
16 | * registry[FINALIZER_REG_KEY] is either nil (no finalizers) or a table | ||
17 | * of functions that Lanes will call after the executing 'pcall' has ended. | ||
18 | * | ||
19 | * We're NOT using the GC system for finalizer mainly because providing the | ||
20 | * error (and maybe stack trace) parameters to the finalizer functions would | ||
21 | * anyways complicate that approach. | ||
22 | */ | ||
23 | // xxh64 of string "kFinalizerRegKey" generated at https://www.pelock.com/products/hash-calculator | ||
24 | static constexpr RegistryUniqueKey kFinalizerRegKey{ 0xFE936BFAA718FEEAull }; | ||
25 | |||
26 | // xxh64 of string "kExtendedStackTraceRegKey" generated at https://www.pelock.com/products/hash-calculator | ||
27 | static constexpr RegistryUniqueKey kExtendedStackTraceRegKey{ 0x38147AD48FB426E2ull }; // used as registry key | ||
28 | |||
29 | // xxh64 of string "kLaneGC" generated at https://www.pelock.com/products/hash-calculator | ||
30 | static constexpr UniqueKey kLaneGC{ 0x5D6122141727F960ull }; | ||
31 | |||
32 | // xxh64 of string "kLanePointerRegKey" generated at https://www.pelock.com/products/hash-calculator | ||
33 | static constexpr RegistryUniqueKey kLanePointerRegKey{ 0x2D8CF03FE9F0A51Aull }; // used as registry key | ||
34 | |||
35 | // ################################################################################################# | ||
36 | |||
37 | // The chain is ended by '(Lane*)(-1)', not nullptr: 'selfdestructFirst -> ... -> ... -> (-1)' | ||
38 | #define SELFDESTRUCT_END ((Lane*) (-1)) | ||
39 | |||
40 | // must be a #define instead of a constexpr to work with lua_pushliteral (until I templatize it) | ||
41 | #define kLaneMetatableName "Lane" | ||
42 | #define kLanesLibName "lanes" | ||
43 | #define kLanesCoreLibName kLanesLibName ".core" | ||
44 | |||
45 | // NOTE: values to be changed by either thread, during execution, without | ||
46 | // locking, are marked "volatile" | ||
47 | // | ||
48 | class Lane | ||
49 | { | ||
50 | public: | ||
51 | /* | ||
52 | Pending: The Lua VM hasn't done anything yet. | ||
53 | Running, Waiting: Thread is inside the Lua VM. If the thread is forcefully stopped, we can't lua_close() the Lua State. | ||
54 | Done, Error, Cancelled: Thread execution is outside the Lua VM. It can be lua_close()d. | ||
55 | */ | ||
56 | enum class Status | ||
57 | { | ||
58 | Pending, | ||
59 | Running, | ||
60 | Waiting, | ||
61 | Done, | ||
62 | Error, | ||
63 | Cancelled | ||
64 | }; | ||
65 | using enum Status; | ||
66 | |||
67 | // the thread | ||
68 | std::jthread thread; | ||
69 | // a latch to wait for the lua_State to be ready | ||
70 | std::latch ready{ 1 }; | ||
71 | // to wait for stop requests through thread's stop_source | ||
72 | std::mutex doneMutex; | ||
73 | std::condition_variable doneCondVar; // use condition_variable_any if waiting for a stop_token | ||
74 | // | ||
75 | // M: sub-thread OS thread | ||
76 | // S: not used | ||
77 | |||
78 | char const* debugName{ "<unnamed>" }; | ||
79 | |||
80 | Universe* const U; | ||
81 | lua_State* L; | ||
82 | // | ||
83 | // M: prepares the state, and reads results | ||
84 | // S: while S is running, M must keep out of modifying the state | ||
85 | |||
86 | Status volatile status{ Pending }; | ||
87 | // | ||
88 | // M: sets to Pending (before launching) | ||
89 | // S: updates -> Running/Waiting -> Done/Error/Cancelled | ||
90 | |||
91 | std::condition_variable* volatile waiting_on{ nullptr }; | ||
92 | // | ||
93 | // When status is Waiting, points on the linda's signal the thread waits on, else nullptr | ||
94 | |||
95 | CancelRequest volatile cancelRequest{ CancelRequest::None }; | ||
96 | // | ||
97 | // M: sets to false, flags true for cancel request | ||
98 | // S: reads to see if cancel is requested | ||
99 | |||
100 | Lane* volatile selfdestruct_next{ nullptr }; | ||
101 | // | ||
102 | // M: sets to non-nullptr if facing lane handle '__gc' cycle but the lane | ||
103 | // is still running | ||
104 | // S: cleans up after itself if non-nullptr at lane exit | ||
105 | |||
106 | #if HAVE_LANE_TRACKING() | ||
107 | Lane* volatile tracking_next{ nullptr }; | ||
108 | #endif // HAVE_LANE_TRACKING() | ||
109 | // | ||
110 | // For tracking only | ||
111 | |||
112 | [[nodiscard]] static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internalAllocator.alloc(size_); } | ||
113 | // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception | ||
114 | static void operator delete(void* p_, Universe* U_) { U_->internalAllocator.free(p_, sizeof(Lane)); } | ||
115 | // this one is for us, to make sure memory is freed by the correct allocator | ||
116 | static void operator delete(void* p_) { static_cast<Lane*>(p_)->U->internalAllocator.free(p_, sizeof(Lane)); } | ||
117 | |||
118 | Lane(Universe* U_, lua_State* L_); | ||
119 | ~Lane(); | ||
120 | |||
121 | void changeDebugName(int nameIdx_); | ||
122 | void pushThreadStatus(lua_State* L_) const; | ||
123 | void securizeDebugName(lua_State* L_); | ||
124 | void startThread(int priority_); | ||
125 | char const* threadStatusString() const; | ||
126 | [[nodiscard]] bool waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_); | ||
127 | |||
128 | static void PushMetatable(lua_State* L_); | ||
129 | }; | ||
130 | |||
131 | // ################################################################################################# | ||
132 | |||
133 | // To allow free-running threads (longer lifespan than the handle's) | ||
134 | // 'Lane' are malloc/free'd and the handle only carries a pointer. | ||
135 | // This is not deep userdata since the handle's not portable among lanes. | ||
136 | // | ||
137 | [[nodiscard]] inline Lane* ToLane(lua_State* L_, int i_) | ||
138 | { | ||
139 | return *(static_cast<Lane**>(luaL_checkudata(L_, i_, kLaneMetatableName))); | ||
140 | } | ||