aboutsummaryrefslogtreecommitdiff
path: root/src/lane.h
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-10-28 18:09:51 +0100
committerBenoit Germain <benoit.germain@ubisoft.com>2024-10-28 18:09:51 +0100
commit3cf9c3c9d076d5822595834bdbf5153d4e923c67 (patch)
tree13ff16c05ff047a569b90e8981bdc349fd573c6e /src/lane.h
parentdf60f71fe943686deed8ca0f85c6d597570ab030 (diff)
downloadlanes-3cf9c3c9d076d5822595834bdbf5153d4e923c67.tar.gz
lanes-3cf9c3c9d076d5822595834bdbf5153d4e923c67.tar.bz2
lanes-3cf9c3c9d076d5822595834bdbf5153d4e923c67.zip
Renamed lane.h → lane.hpp, linda.h → linda.hpp, threading.h → threading.hpp
Diffstat (limited to 'src/lane.h')
-rw-r--r--src/lane.h209
1 files changed, 0 insertions, 209 deletions
diff --git a/src/lane.h b/src/lane.h
deleted file mode 100644
index 7821112..0000000
--- a/src/lane.h
+++ /dev/null
@@ -1,209 +0,0 @@
1#pragma once
2
3#include "cancel.hpp"
4#include "uniquekey.hpp"
5#include "universe.hpp"
6
7// #################################################################################################
8
9// xxh64 of string "kExtendedStackTraceRegKey" generated at https://www.pelock.com/products/hash-calculator
10static constexpr RegistryUniqueKey kExtendedStackTraceRegKey{ 0x38147AD48FB426E2ull }; // used as registry key
11
12/*
13 * registry[FINALIZER_REG_KEY] is either nil (no finalizers) or a table
14 * of functions that Lanes will call after the executing 'pcall' has ended.
15 *
16 * We're NOT using the GC system for finalizer mainly because providing the
17 * error (and maybe stack trace) arguments to the finalizer functions would
18 * anyways complicate that approach.
19 */
20// xxh64 of string "kCoroutineRegKey" generated at https://www.pelock.com/products/hash-calculator
21static constexpr RegistryUniqueKey kCoroutineRegKey{ 0x72B049B0D130F009ull };
22
23// xxh64 of string "kFinalizerRegKey" generated at https://www.pelock.com/products/hash-calculator
24static constexpr RegistryUniqueKey kFinalizerRegKey{ 0xFE936BFAA718FEEAull };
25
26// xxh64 of string "kLaneGC" generated at https://www.pelock.com/products/hash-calculator
27static constexpr UniqueKey kLaneGC{ 0x5D6122141727F960ull };
28
29// xxh64 of string "kLanePointerRegKey" generated at https://www.pelock.com/products/hash-calculator
30static constexpr RegistryUniqueKey kLanePointerRegKey{ 0x2D8CF03FE9F0A51Aull }; // used as registry key
31
32 // xxh64 of string "debugName" generated at https://www.pelock.com/products/hash-calculator
33static constexpr RegistryUniqueKey kLaneNameRegKey{ 0xA194E2645C57F6DDull };
34
35// #################################################################################################
36
37// The chain is ended by '(Lane*)(-1)', not nullptr: 'selfdestructFirst -> ... -> ... -> (-1)'
38#define SELFDESTRUCT_END ((Lane*) (-1))
39
40static constexpr std::string_view kLaneMetatableName{ "Lane" };
41
42// must be a #define instead of a constexpr to benefit from compile-time string concatenation
43#define kLanesLibName "lanes"
44#define kLanesCoreLibName kLanesLibName ".core"
45
46// for cancel() argument
47enum class WakeLane
48{
49 No,
50 Yes
51};
52
53// NOTE: values to be changed by either thread, during execution, without locking, are marked "volatile"
54class Lane
55{
56 public:
57 /*
58 Pending: The Lua VM hasn't done anything yet.
59 Resuming: The user requested the lane to resume execution from Suspended state.
60 Suspended: returned from lua_resume, waiting for the client to request a lua_resume.
61 Running, Suspended, Waiting: Thread is inside the Lua VM.
62 Done, Error, Cancelled: Thread execution is outside the Lua VM. It can be lua_close()d.
63 */
64 enum class Status
65 {
66 Pending,
67 Running,
68 Suspended,
69 Resuming,
70 Waiting,
71 Done,
72 Error,
73 Cancelled
74 };
75 using enum Status;
76
77 enum class ErrorTraceLevel
78 {
79 Minimal, // no error handler function when running the lane body
80 Basic, // lane body errors caught by a error handler
81 Extended // same as above, but with more data extracted from the debug infos
82 };
83 using enum ErrorTraceLevel;
84
85 // the thread
86 std::thread thread; // use jthread if we ever need a stop_source
87#ifndef __PROSPERO__
88 // a latch to wait for the lua_State to be ready
89 std::latch ready{ 1 };
90#else // __PROSPERO__
91 std::atomic_flag ready{};
92#endif // __PROSPERO__
93 // to wait for stop requests through thread's stop_source
94 std::mutex doneMutex;
95 std::condition_variable doneCondVar; // use condition_variable_any if waiting for a stop_token
96 //
97 // M: sub-thread OS thread
98 // S: not used
99
100 private:
101
102 mutable std::mutex debugNameMutex;
103 std::string_view debugName{ "<unnamed>" };
104
105 public:
106
107 Universe* const U{};
108 lua_State* S{}; // the master state of the lane
109 lua_State* L{}; // the state we run things in (either S or a lua_newthread() state if we run in coroutine mode)
110 int nresults{}; // number of results to extract from the stack (can differ from the actual number of values when in coroutine mode)
111 //
112 // M: prepares the state, and reads results
113 // S: while S is running, M must keep out of modifying the state
114
115 Status volatile status{ Pending };
116 //
117 // M: sets to Pending (before launching)
118 // S: updates -> Running/Waiting/Suspended -> Done/Error/Cancelled
119
120 std::condition_variable* volatile waiting_on{ nullptr };
121 //
122 // When status is Waiting, points on the linda's signal the thread waits on, else nullptr
123
124 CancelRequest volatile cancelRequest{ CancelRequest::None };
125 //
126 // M: sets to false, flags true for cancel request
127 // S: reads to see if cancel is requested
128
129 Lane* volatile selfdestruct_next{ nullptr };
130 //
131 // M: sets to non-nullptr if facing lane handle '__gc' cycle but the lane
132 // is still running
133 // S: cleans up after itself if non-nullptr at lane exit
134
135 // For tracking only
136 Lane* volatile tracking_next{ nullptr };
137
138 ErrorTraceLevel const errorTraceLevel{ Basic };
139
140 [[nodiscard]] static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internalAllocator.alloc(size_); }
141 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
142 static void operator delete(void* p_, Universe* U_) { U_->internalAllocator.free(p_, sizeof(Lane)); }
143 // this one is for us, to make sure memory is freed by the correct allocator
144 static void operator delete(void* p_) { static_cast<Lane*>(p_)->U->internalAllocator.free(p_, sizeof(Lane)); }
145
146 ~Lane();
147 Lane(Universe* U_, lua_State* L_, ErrorTraceLevel errorTraceLevel_, bool asCoroutine_);
148
149 // rule of 5
150 Lane(Lane const&) = delete;
151 Lane(Lane&&) = delete;
152 Lane& operator=(Lane const&) = delete;
153 Lane& operator=(Lane&&) = delete;
154
155 private:
156
157 [[nodiscard]] CancelResult internalCancel(CancelRequest rq_, std::chrono::time_point<std::chrono::steady_clock> until_, WakeLane wakeLane_);
158
159 public:
160
161 CancelResult cancel(CancelOp op_, std::chrono::time_point<std::chrono::steady_clock> until_, WakeLane wakeLane_, int hookCount_);
162 void changeDebugName(StackIndex nameIdx_);
163 void closeState()
164 {
165 lua_State* const _L{ S };
166 S = nullptr;
167 L = nullptr;
168 nresults = 0;
169 lua_close(_L); // this collects our coroutine thread at the same time
170 }
171 [[nodiscard]] std::string_view errorTraceLevelString() const;
172 [[nodiscard]] int errorHandlerCount() const noexcept
173 {
174 // don't push a error handler when in coroutine mode, as the first lua_resume wants only the function and its arguments on the stack
175 return ((errorTraceLevel == Lane::Minimal) || isCoroutine()) ? 0 : 1;
176 }
177 [[nodiscard]] bool isCoroutine() const noexcept { return S != L; }
178 [[nodiscard]] std::string_view getDebugName() const
179 {
180 std::lock_guard<std::mutex> _guard{ debugNameMutex };
181 return debugName;
182 }
183 static int LuaErrorHandler(lua_State* L_);
184 [[nodiscard]] int pushErrorHandler() const noexcept { return (errorHandlerCount() == 0) ? 0 : (lua_pushcfunction(L, LuaErrorHandler), 1); }
185 [[nodiscard]] std::string_view pushErrorTraceLevel(lua_State* L_) const;
186 static void PushMetatable(lua_State* L_);
187 void pushStatusString(lua_State* L_) const;
188 void pushIndexedResult(lua_State* L_, int key_) const;
189 void resetResultsStorage(lua_State* L_, StackIndex self_idx_);
190 void selfdestructAdd();
191 [[nodiscard]] bool selfdestructRemove();
192 void securizeDebugName(lua_State* L_);
193 void startThread(int priority_);
194 [[nodiscard]] int storeResults(lua_State* L_);
195 [[nodiscard]] std::string_view threadStatusString() const;
196 // wait until the lane stops working with its state (either Suspended or Done+)
197 [[nodiscard]] bool waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_);
198};
199
200// #################################################################################################
201
202// To allow free-running threads (longer lifespan than the handle's)
203// 'Lane' are malloc/free'd and the handle only carries a pointer.
204// This is not deep userdata since the handle is not portable among lanes.
205//
206[[nodiscard]] inline Lane* ToLane(lua_State* const L_, StackIndex const i_)
207{
208 return *(static_cast<Lane**>(luaL_checkudata(L_, i_, kLaneMetatableName.data())));
209}