diff options
Diffstat (limited to 'src/universe.cpp')
-rw-r--r-- | src/universe.cpp | 116 |
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 | ||
8 | Copyright (C) 2017 Benoit Germain <bnt.germain@gmail.com> | 8 | Copyright (C) 2017-2024 Benoit Germain <bnt.germain@gmail.com> |
9 | 9 | ||
10 | Permission is hereby granted, free of charge, to any person obtaining a copy | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy |
11 | of this software and associated documentation files (the "Software"), to deal | 11 | of 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" |
40 | static 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" |
42 | static 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 |
75 | Universe* 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 | ||
89 | void universe_store(lua_State* L_, Universe* U_) | 85 | void 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 | ||
99 | Universe* universe_get(lua_State* L_) | 151 | // process end: cancel any still free-running threads |
152 | int 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 | } |