aboutsummaryrefslogtreecommitdiff
path: root/src/universe.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/universe.cpp')
-rw-r--r--src/universe.cpp116
1 files changed, 93 insertions, 23 deletions
diff --git a/src/universe.cpp b/src/universe.cpp
index bf64560..6adc314 100644
--- a/src/universe.cpp
+++ b/src/universe.cpp
@@ -1,11 +1,11 @@
1/* 1/*
2 * UNIVERSE.C Copyright (c) 2017, Benoit Germain 2 * UNIVERSE.CPP Copyright (c) 2017-2024, Benoit Germain
3 */ 3 */
4 4
5/* 5/*
6=============================================================================== 6===============================================================================
7 7
8Copyright (C) 2017 Benoit Germain <bnt.germain@gmail.com> 8Copyright (C) 2017-2024 Benoit Germain <bnt.germain@gmail.com>
9 9
10Permission is hereby granted, free of charge, to any person obtaining a copy 10Permission is hereby granted, free of charge, to any person obtaining a copy
11of this software and associated documentation files (the "Software"), to deal 11of this software and associated documentation files (the "Software"), to deal
@@ -28,18 +28,14 @@ THE SOFTWARE.
28=============================================================================== 28===============================================================================
29*/ 29*/
30 30
31#include <string.h>
32#include <assert.h>
33
34#include "universe.h" 31#include "universe.h"
35#include "compat.h"
36#include "macros_and_utils.h"
37#include "uniquekey.h"
38 32
39// xxh64 of string "kUniverseFullRegKey" generated at https://www.pelock.com/products/hash-calculator 33#include "cancel.h"
40static constexpr RegistryUniqueKey kUniverseFullRegKey{ 0x1C2D76870DD9DD9Full }; 34#include "compat.h"
41// xxh64 of string "kUniverseLightRegKey" generated at https://www.pelock.com/products/hash-calculator 35#include "deep.h"
42static constexpr RegistryUniqueKey kUniverseLightRegKey{ 0x48BBE9CEAB0BA04Full }; 36#include "keeper.h"
37#include "lanes_private.h"
38#include "tools.h"
43 39
44// ################################################################################################# 40// #################################################################################################
45 41
@@ -72,7 +68,7 @@ Universe::Universe()
72// ################################################################################################# 68// #################################################################################################
73 69
74// only called from the master state 70// only called from the master state
75Universe* universe_create(lua_State* L_) 71[[nodiscard]] Universe* universe_create(lua_State* L_)
76{ 72{
77 LUA_ASSERT(L_, universe_get(L_) == nullptr); 73 LUA_ASSERT(L_, universe_get(L_) == nullptr);
78 Universe* const U{ lua_newuserdatauv<Universe>(L_, 0) }; // universe 74 Universe* const U{ lua_newuserdatauv<Universe>(L_, 0) }; // universe
@@ -86,20 +82,94 @@ Universe* universe_create(lua_State* L_)
86 82
87// ################################################################################################# 83// #################################################################################################
88 84
89void universe_store(lua_State* L_, Universe* U_) 85void Universe::terminateFreeRunningLanes(lua_State* L_, lua_Duration shutdownTimeout_, CancelOp op_)
90{ 86{
91 LUA_ASSERT(L_, !U_ || universe_get(L_) == nullptr); 87 if (selfdestructFirst != SELFDESTRUCT_END) {
92 STACK_CHECK_START_REL(L_, 0); 88 // Signal _all_ still running threads to exit (including the timer thread)
93 kUniverseLightRegKey.setValue(L_, [U = U_](lua_State* L_) { U ? lua_pushlightuserdata(L_, U) : lua_pushnil(L_); }); 89 {
94 STACK_CHECK(L_, 0); 90 std::lock_guard<std::mutex> guard{ selfdestructMutex };
91 Lane* lane{ selfdestructFirst };
92 lua_Duration timeout{ 1us };
93 while (lane != SELFDESTRUCT_END) {
94 // attempt the requested cancel with a small timeout.
95 // if waiting on a linda, they will raise a cancel_error.
96 // if a cancellation hook is desired, it will be installed to try to raise an error
97 if (lane->thread.joinable()) {
98 std::ignore = thread_cancel(lane, op_, 1, timeout, true);
99 }
100 lane = lane->selfdestruct_next;
101 }
102 }
103
104 // When noticing their cancel, the lanes will remove themselves from the selfdestruct chain.
105 {
106 std::chrono::time_point<std::chrono::steady_clock> t_until{ std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(shutdownTimeout_) };
107
108 while (selfdestructFirst != SELFDESTRUCT_END) {
109 // give threads time to act on their cancel
110 std::this_thread::yield();
111 // count the number of cancelled thread that didn't have the time to act yet
112 int n{ 0 };
113 {
114 std::lock_guard<std::mutex> guard{ selfdestructMutex };
115 Lane* lane{ selfdestructFirst };
116 while (lane != SELFDESTRUCT_END) {
117 if (lane->cancelRequest != CancelRequest::None)
118 ++n;
119 lane = lane->selfdestruct_next;
120 }
121 }
122 // if timeout elapsed, or we know all threads have acted, stop waiting
123 std::chrono::time_point<std::chrono::steady_clock> t_now = std::chrono::steady_clock::now();
124 if (n == 0 || (t_now >= t_until)) {
125 DEBUGSPEW_CODE(fprintf(stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdownTimeout_.count()));
126 break;
127 }
128 }
129 }
130
131 // If some lanes are currently cleaning after themselves, wait until they are done.
132 // They are no longer listed in the selfdestruct chain, but they still have to lua_close().
133 while (selfdestructingCount.load(std::memory_order_acquire) > 0) {
134 std::this_thread::yield();
135 }
136 }
137
138 // If after all this, we still have some free-running lanes, it's an external user error, they should have stopped appropriately
139 {
140 std::lock_guard<std::mutex> guard{ selfdestructMutex };
141 Lane* lane{ selfdestructFirst };
142 if (lane != SELFDESTRUCT_END) {
143 // this causes a leak because we don't call U's destructor (which could be bad if the still running lanes are accessing it)
144 raise_luaL_error(L_, "Zombie thread %s refuses to die!", lane->debugName);
145 }
146 }
95} 147}
96 148
97// ################################################################################################# 149// #################################################################################################
98 150
99Universe* universe_get(lua_State* L_) 151// process end: cancel any still free-running threads
152int universe_gc(lua_State* L_)
100{ 153{
101 STACK_CHECK_START_REL(L_, 0); 154 lua_Duration const shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) };
102 Universe* const universe{ kUniverseLightRegKey.readLightUserDataValue<Universe>(L_) }; 155 [[maybe_unused]] char const* const op_string{ lua_tostring(L_, lua_upvalueindex(2)) };
103 STACK_CHECK(L_, 0); 156 Universe* const U{ lua_tofulluserdata<Universe>(L_, 1) };
104 return universe; 157 U->terminateFreeRunningLanes(L_, shutdown_timeout, which_cancel_op(op_string));
158
159 // no need to mutex-protect this as all threads in the universe are gone at that point
160 if (U->timerLinda != nullptr) { // test in case some early internal error prevented Lanes from creating the deep timer
161 [[maybe_unused]] int const prev_ref_count{ U->timerLinda->refcount.fetch_sub(1, std::memory_order_relaxed) };
162 LUA_ASSERT(L_, prev_ref_count == 1); // this should be the last reference
163 DeepFactory::DeleteDeepObject(L_, U->timerLinda);
164 U->timerLinda = nullptr;
165 }
166
167 close_keepers(U);
168
169 // remove the protected allocator, if any
170 U->protectedAllocator.removeFrom(L_);
171
172 U->Universe::~Universe();
173
174 return 0;
105} 175}