diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-07 09:42:09 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-13 18:15:46 +0200 |
| commit | d093c5555ec439affcfbecdceabfb122aa8c2f73 (patch) | |
| tree | de148388b0382d87bcc2caed72625e3dec829bda /src/universe.cpp | |
| parent | ebb0837588336e32fc1258a3838673afdd31ee71 (diff) | |
| download | lanes-d093c5555ec439affcfbecdceabfb122aa8c2f73.tar.gz lanes-d093c5555ec439affcfbecdceabfb122aa8c2f73.tar.bz2 lanes-d093c5555ec439affcfbecdceabfb122aa8c2f73.zip | |
Some more code refactorization
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 | } |
