Toast Engine
Loading...
Searching...
No Matches
lua_state.hpp
Go to the documentation of this file.
1
21
22#pragma once
23
24#include <atomic>
25#include <cstdint>
26#include <functional>
27#include <memory>
28#include <mutex>
29#include <optional>
30#include <string>
31#include <string_view>
32#include <toast/export.hpp>
33#include <toast/log.hpp>
34#include <toast/thread_pool.hpp>
35#include <unordered_map>
36#include <vector>
37
38struct lua_State;
39
40namespace scripting {
41
42class TOAST_API LuaState {
43 struct Entry;
44
45public:
47 class TOAST_API Lock {
48 public:
49 Lock() = default;
50 Lock(Lock&& other) noexcept;
51 auto operator=(Lock&& other) noexcept -> Lock&;
52 Lock(const Lock&) = delete;
53 auto operator=(const Lock&) -> Lock& = delete;
54 ~Lock();
55
56 explicit operator bool() const noexcept { return m_entry != nullptr; }
57
58 [[nodiscard]]
59 auto state() const noexcept -> lua_State* {
60 return m_state;
61 }
62
63 [[nodiscard]]
64 auto index() const noexcept -> size_t {
65 return m_index;
66 }
67
69 void unlock() noexcept;
70
71 private:
72 friend class LuaState;
73 Lock(Entry* entry, lua_State* state, size_t index) noexcept;
74
75 Entry* m_entry = nullptr;
76 lua_State* m_state = nullptr;
77 size_t m_index = 0;
78 };
79
80 static auto create() noexcept -> std::unique_ptr<LuaState>;
81 static auto get() noexcept -> LuaState&;
82
83 [[nodiscard]]
84 static auto exists() noexcept -> bool {
85 return instance != nullptr;
86 }
87
88 ~LuaState() noexcept;
89
96 [[nodiscard]]
97 auto lock(size_t index) noexcept -> Lock;
98
100 [[nodiscard]]
101 auto tryLock(size_t index) noexcept -> Lock;
102
103 [[nodiscard]]
104 static auto indexOf(lua_State* state) noexcept -> std::optional<size_t>;
105
107 [[nodiscard]]
108 static auto ownedByCurrentThread(size_t index) noexcept -> bool;
109 [[nodiscard]]
110 static auto ownedByCurrentThread(lua_State* state) noexcept -> bool;
111
113 [[nodiscard]]
114 static auto ownsAnyState() noexcept -> bool;
115
117 [[nodiscard]]
118 static auto lastOwnedIndex() noexcept -> std::optional<size_t>;
119
121 static constexpr uint32_t k_group_chunk = 8;
122
135 [[nodiscard]]
136 auto assign(uint64_t group, std::optional<size_t> forced = std::nullopt) -> size_t;
137
139 void unassign(uint64_t group, size_t index) noexcept;
140
142 [[nodiscard]]
143 auto loads() const -> std::vector<uint32_t>;
144
146 [[nodiscard]]
147 static auto newGroup() noexcept -> uint64_t;
148
149 [[nodiscard]]
150 auto poolSize() const noexcept -> size_t {
151 return m_pool_size;
152 }
153
155 [[nodiscard]]
156 auto mainState(size_t index) const noexcept -> lua_State*;
157
163 void retire(size_t index, std::move_only_function<void()> destroyer) noexcept;
164
166 static void retireOrLeak(size_t index, std::move_only_function<void()> destroyer) noexcept;
167
169 [[nodiscard]]
170 auto contentionCount() const noexcept -> uint64_t {
171 return m_contention.load(std::memory_order_relaxed);
172 }
173
175 [[nodiscard]]
176 auto waitCount() const noexcept -> uint64_t {
177 return m_waits.load(std::memory_order_relaxed);
178 }
179
183 class TOAST_API NonBlockingScope {
184 public:
185 NonBlockingScope() noexcept;
186 ~NonBlockingScope();
187
188 NonBlockingScope(const NonBlockingScope&) = delete;
189 auto operator=(const NonBlockingScope&) -> NonBlockingScope& = delete;
190 NonBlockingScope(NonBlockingScope&&) = delete;
191 auto operator=(NonBlockingScope&&) -> NonBlockingScope& = delete;
192 };
193
195 auto runString(std::string_view lua_code) noexcept -> bool;
196
198 auto runStringOn(size_t index, std::string_view lua_code, std::string* error = nullptr) noexcept -> bool;
199
201 void refreshTypeMarkers() noexcept;
202
203private:
204 static inline LuaState* instance = nullptr;
205
206 struct Entry {
207 lua_State* state = nullptr;
208 std::recursive_mutex mutex;
209 std::atomic<uint64_t> owner {0};
210 int depth = 0;
211 std::mutex pending_mutex;
212 std::vector<std::move_only_function<void()>> pending;
213 std::atomic<size_t> pending_count {0};
214 };
215
216 size_t m_pool_size = 0;
217 std::vector<Entry> m_entries;
218 std::atomic<uint64_t> m_contention {0};
219 std::atomic<uint64_t> m_waits {0};
220
222 struct GroupPlacement {
223 std::vector<uint32_t> per_index;
224 size_t index = 0;
225 uint32_t members = 0;
226 };
227
228 // Guards the placement bookkeeping, which only changes while runtimes are created and destroyed
229 mutable std::mutex m_placement_mutex;
230 std::vector<uint32_t> m_loads;
231 std::unordered_map<uint64_t, GroupPlacement> m_groups;
232 size_t m_next_tie = 0;
233
234 LuaState();
235
236 auto acquire(size_t index, bool wait) noexcept -> Lock;
237 static void release(Entry& entry, size_t index) noexcept;
238 static void drainPending(Entry& entry) noexcept;
239
240 static void registerApi(lua_State* state) noexcept;
241 static void registerTypeMarkers(lua_State* state) noexcept;
242};
243
244}
245
249#ifdef _DEBUG
250#define TOAST_LUA_ASSERT_OWNED(state) \
251 TOAST_ASSERT( \
252 ::scripting::LuaState::ownedByCurrentThread(state), "Lua", "A Lua interpreter was used by a thread that does not own it" \
253 )
254#elif defined(TOAST_LUA_OWNERSHIP_CHECKS)
255#define TOAST_LUA_ASSERT_OWNED(state) \
256 do { \
257 if (!::scripting::LuaState::ownedByCurrentThread(state)) { \
258 TOAST_CRITICAL("Lua", "A Lua interpreter was used by a thread that does not own it"); \
259 } \
260 } while (0)
261#else
262#define TOAST_LUA_ASSERT_OWNED(state) ((void)(state))
263#endif
void unlock() noexcept
Gives the interpreter back before the end of the scope.
Definition lua_state.cpp:211
Definition lua_state.hpp:42
static auto newGroup() noexcept -> uint64_t
Allocates the number of a new script group, never zero.
Definition lua_state.cpp:450
auto assign(uint64_t group, std::optional< size_t > forced=std::nullopt) -> size_t
Picks the interpreter for a new script runtime.
Definition lua_state.cpp:378
auto waitCount() const noexcept -> uint64_t
How many times a thread had to wait for an interpreter that another thread owned.
Definition lua_state.hpp:176
void refreshTypeMarkers() noexcept
Re-registers the Node/Asset type-marker globals on every state.
Definition lua_state.cpp:1341
void unassign(uint64_t group, size_t index) noexcept
Gives back what assign() handed out.
Definition lua_state.cpp:429
auto runStringOn(size_t index, std::string_view lua_code, std::string *error=nullptr) noexcept -> bool
Runs a chunk on the given interpreter.
Definition lua_state.cpp:459
auto runString(std::string_view lua_code) noexcept -> bool
Runs a chunk on state 0; intended for debug/console use.
Definition lua_state.cpp:455
auto loads() const -> std::vector< uint32_t >
How many runtimes every interpreter hosts, for diagnostics and tests.
Definition lua_state.cpp:424
auto contentionCount() const noexcept -> uint64_t
How many times a thread that owned an interpreter found another one busy; non zero means work was que...
Definition lua_state.hpp:170
static constexpr uint32_t k_group_chunk
How many runtimes of one script group share an interpreter before the next ones go to another.
Definition lua_state.hpp:121
Public logging interface for the engine.
Thread pool implementation for parallel task execution.
@ release
the input went from on to off
Definition value.hpp:79