diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-07-01 09:31:48 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-07-01 09:31:48 +0200 |
| commit | a29b16db27c0cd3c6cf1d1a4d8551174282bd296 (patch) | |
| tree | 368e0533cd7007fb399a03c864c346974d8dd1c8 | |
| parent | ac12af5c39b0689edb931fbe9a162db5687d392f (diff) | |
| download | lanes-a29b16db27c0cd3c6cf1d1a4d8551174282bd296.tar.gz lanes-a29b16db27c0cd3c6cf1d1a4d8551174282bd296.tar.bz2 lanes-a29b16db27c0cd3c6cf1d1a4d8551174282bd296.zip | |
lanes.finally() handler decides whether to thow or freeze
| -rw-r--r-- | docs/index.html | 7 | ||||
| -rw-r--r-- | src/universe.cpp | 24 |
2 files changed, 17 insertions, 14 deletions
diff --git a/docs/index.html b/docs/index.html index 1cc007f..0cdf954 100644 --- a/docs/index.html +++ b/docs/index.html | |||
| @@ -379,7 +379,7 @@ | |||
| 379 | <td> | 379 | <td> |
| 380 | Sets the duration in seconds Lanes will wait for graceful termination of running lanes at application shutdown. Default is <tt>0.25</tt>.<br /> | 380 | Sets the duration in seconds Lanes will wait for graceful termination of running lanes at application shutdown. Default is <tt>0.25</tt>.<br /> |
| 381 | Lanes signals all lanes for cancellation with <tt>"soft"</tt>, <tt>"hard"</tt>, and <tt>"all"</tt> modes, in that order. Each attempt has <tt>shutdown_timeout</tt> seconds to succeed before the next one.<br /> | 381 | Lanes signals all lanes for cancellation with <tt>"soft"</tt>, <tt>"hard"</tt>, and <tt>"all"</tt> modes, in that order. Each attempt has <tt>shutdown_timeout</tt> seconds to succeed before the next one.<br /> |
| 382 | Then there is a last chance at cleanup with <a href="#finally"><tt>lanes.finally()</tt></a>. If some lanes are still running after that point, shutdown will freeze the application forever. It is YOUR responsibility to cleanup properly after yourself. | 382 | Then there is a last chance at cleanup with <a href="#finally"><tt>lanes.finally()</tt></a>. If some lanes are still running after that point, shutdown will either freeze or throw. It is YOUR responsibility to cleanup properly after yourself. |
| 383 | </td> | 383 | </td> |
| 384 | </tr> | 384 | </tr> |
| 385 | 385 | ||
| @@ -469,9 +469,10 @@ | |||
| 469 | <p> | 469 | <p> |
| 470 | An error will be raised if you attempt to do this from inside a lane, or on bad arguments (non-function, or too many arguments).<br /> | 470 | An error will be raised if you attempt to do this from inside a lane, or on bad arguments (non-function, or too many arguments).<br /> |
| 471 | Only the last registered finalizer is kept. It can be cleared by passing <tt>nil</tt> or nothing.<br /> | 471 | Only the last registered finalizer is kept. It can be cleared by passing <tt>nil</tt> or nothing.<br /> |
| 472 | The installed function is called after all free-running lanes got a chance to terminate (see<a href="#shutdown_timeout"><tt>shutdown_timeout</tt></a>), but before lindas become unusable.<br /> | 472 | The finalizer is called unprotected from inside <tt>__gc</tt> metamethod of Lane's Universe. Therefore, if your finalizer raises an error, Lua rules regarding errors in finalizers apply normally.<br /> |
| 473 | The installed function is called after all free-running lanes got a chance to terminate (see <a href="#shutdown_timeout"><tt>shutdown_timeout</tt></a>), but before lindas become unusable.<br /> | ||
| 473 | The finalizer receives a single argument, a <tt>bool</tt> indicating whether some Lanes are still running or not at that point. It is possible to inspect them with <a href="#tracking">tracking</a>.<br /> | 474 | The finalizer receives a single argument, a <tt>bool</tt> indicating whether some Lanes are still running or not at that point. It is possible to inspect them with <a href="#tracking">tracking</a>.<br /> |
| 474 | If an error occurs inside this finalizer, it is silently swallowed, since it happens only during state shutdown, and you can't do anything about it. | 475 | If there are still running lanes when the finalizer returns: Lanes will throw a C++ <tt>std::logic_error</tt> if the finalizer returned <tt>"throw"</tt>. Any other value will cause Lanes to freeze forever. |
| 475 | </p> | 476 | </p> |
| 476 | 477 | ||
| 477 | <hr/> | 478 | <hr/> |
diff --git a/src/universe.cpp b/src/universe.cpp index 5fda29a..04db10f 100644 --- a/src/universe.cpp +++ b/src/universe.cpp | |||
| @@ -427,22 +427,24 @@ LUAG_FUNC(universe_gc) | |||
| 427 | kFinalizerRegKey.pushValue(L_); // L_: U finalizer|nil | 427 | kFinalizerRegKey.pushValue(L_); // L_: U finalizer|nil |
| 428 | if (!lua_isnil(L_, -1)) { | 428 | if (!lua_isnil(L_, -1)) { |
| 429 | lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool | 429 | lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool |
| 430 | lua_pcall(L_, 1, 0, 0); // L_: U | 430 | // no protection. Lua rules for errors in finalizers apply normally |
| 431 | // discard any error that might have occured | 431 | lua_call(L_, 1, 1); // L_: U ret|error |
| 432 | lua_settop(L_, 1); | ||
| 433 | } else { | ||
| 434 | lua_pop(L_, 1); // L_: U | ||
| 435 | } | 432 | } |
| 436 | // in case of error, the message is pushed on the stack | 433 | STACK_CHECK(L_, 2); |
| 437 | STACK_CHECK(L_, 1); | 434 | |
| 435 | // if some lanes are still running here, we have no other choice than crashing or freezing and let the client figure out what's wrong | ||
| 436 | bool const _throw{ luaG_tostring(L_, -1) == "throw" }; | ||
| 437 | lua_pop(L_, 1); // L_: U | ||
| 438 | 438 | ||
| 439 | // if some lanes are still running here, we have no other choice than crashing and let the client figure out what's wrong | ||
| 440 | while (_U->selfdestructFirst != SELFDESTRUCT_END) { | 439 | while (_U->selfdestructFirst != SELFDESTRUCT_END) { |
| 441 | throw std::logic_error{ "Some lanes are still running at shutdown" }; | 440 | if (_throw) { |
| 442 | //std::this_thread::yield(); | 441 | throw std::logic_error{ "Some lanes are still running at shutdown" }; |
| 442 | } else { | ||
| 443 | std::this_thread::yield(); | ||
| 444 | } | ||
| 443 | } | 445 | } |
| 444 | 446 | ||
| 445 | // no need to mutex-protect this as all threads in the universe are gone at that point | 447 | // no need to mutex-protect this as all lanes in the universe are gone at that point |
| 446 | if (_U->timerLinda != nullptr) { // test in case some early internal error prevented Lanes from creating the deep timer | 448 | if (_U->timerLinda != nullptr) { // test in case some early internal error prevented Lanes from creating the deep timer |
| 447 | [[maybe_unused]] int const _prev_ref_count{ _U->timerLinda->refcount.fetch_sub(1, std::memory_order_relaxed) }; | 449 | [[maybe_unused]] int const _prev_ref_count{ _U->timerLinda->refcount.fetch_sub(1, std::memory_order_relaxed) }; |
| 448 | LUA_ASSERT(L_, _prev_ref_count == 1); // this should be the last reference | 450 | LUA_ASSERT(L_, _prev_ref_count == 1); // this should be the last reference |
