diff options
Diffstat (limited to 'src/lanes.cpp')
-rw-r--r-- | src/lanes.cpp | 135 |
1 files changed, 18 insertions, 117 deletions
diff --git a/src/lanes.cpp b/src/lanes.cpp index bce75a6..90f0f9f 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp | |||
@@ -393,14 +393,6 @@ static void push_stack_trace(lua_State* L_, int rc_, int stk_base_) | |||
393 | // ########################################### Threads ############################################# | 393 | // ########################################### Threads ############################################# |
394 | // ################################################################################################# | 394 | // ################################################################################################# |
395 | 395 | ||
396 | // | ||
397 | // Protects modifying the selfdestruct chain | ||
398 | |||
399 | #define SELFDESTRUCT_END ((Lane*) (-1)) | ||
400 | // | ||
401 | // The chain is ended by '(Lane*)(-1)', not nullptr: | ||
402 | // 'selfdestructFirst -> ... -> ... -> (-1)' | ||
403 | |||
404 | /* | 396 | /* |
405 | * Add the lane to selfdestruct chain; the ones still running at the end of the | 397 | * Add the lane to selfdestruct chain; the ones still running at the end of the |
406 | * whole process will be cancelled. | 398 | * whole process will be cancelled. |
@@ -446,99 +438,6 @@ static void selfdestruct_add(Lane* lane_) | |||
446 | 438 | ||
447 | // ################################################################################################# | 439 | // ################################################################################################# |
448 | 440 | ||
449 | // process end: cancel any still free-running threads | ||
450 | [[nodiscard]] static int universe_gc(lua_State* L_) | ||
451 | { | ||
452 | Universe* const U{ lua_tofulluserdata<Universe>(L_, 1) }; | ||
453 | lua_Duration const shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; | ||
454 | [[maybe_unused]] char const* const op_string{ lua_tostring(L_, lua_upvalueindex(2)) }; | ||
455 | CancelOp const op{ which_cancel_op(op_string) }; | ||
456 | |||
457 | if (U->selfdestructFirst != SELFDESTRUCT_END) { | ||
458 | // Signal _all_ still running threads to exit (including the timer thread) | ||
459 | { | ||
460 | std::lock_guard<std::mutex> guard{ U->selfdestructMutex }; | ||
461 | Lane* lane{ U->selfdestructFirst }; | ||
462 | lua_Duration timeout{ 1us }; | ||
463 | while (lane != SELFDESTRUCT_END) { | ||
464 | // attempt the requested cancel with a small timeout. | ||
465 | // if waiting on a linda, they will raise a cancel_error. | ||
466 | // if a cancellation hook is desired, it will be installed to try to raise an error | ||
467 | if (lane->thread.joinable()) { | ||
468 | std::ignore = thread_cancel(lane, op, 1, timeout, true); | ||
469 | } | ||
470 | lane = lane->selfdestruct_next; | ||
471 | } | ||
472 | } | ||
473 | |||
474 | // When noticing their cancel, the lanes will remove themselves from the selfdestruct chain. | ||
475 | { | ||
476 | std::chrono::time_point<std::chrono::steady_clock> t_until{ std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(shutdown_timeout) }; | ||
477 | |||
478 | while (U->selfdestructFirst != SELFDESTRUCT_END) { | ||
479 | // give threads time to act on their cancel | ||
480 | std::this_thread::yield(); | ||
481 | // count the number of cancelled thread that didn't have the time to act yet | ||
482 | int n{ 0 }; | ||
483 | { | ||
484 | std::lock_guard<std::mutex> guard{ U->selfdestructMutex }; | ||
485 | Lane* lane{ U->selfdestructFirst }; | ||
486 | while (lane != SELFDESTRUCT_END) { | ||
487 | if (lane->cancelRequest != CancelRequest::None) | ||
488 | ++n; | ||
489 | lane = lane->selfdestruct_next; | ||
490 | } | ||
491 | } | ||
492 | // if timeout elapsed, or we know all threads have acted, stop waiting | ||
493 | std::chrono::time_point<std::chrono::steady_clock> t_now = std::chrono::steady_clock::now(); | ||
494 | if (n == 0 || (t_now >= t_until)) { | ||
495 | DEBUGSPEW_CODE(fprintf(stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout.count())); | ||
496 | break; | ||
497 | } | ||
498 | } | ||
499 | } | ||
500 | |||
501 | // If some lanes are currently cleaning after themselves, wait until they are done. | ||
502 | // They are no longer listed in the selfdestruct chain, but they still have to lua_close(). | ||
503 | while (U->selfdestructingCount.load(std::memory_order_acquire) > 0) { | ||
504 | std::this_thread::yield(); | ||
505 | } | ||
506 | } | ||
507 | |||
508 | // If after all this, we still have some free-running lanes, it's an external user error, they should have stopped appropriately | ||
509 | { | ||
510 | std::lock_guard<std::mutex> guard{ U->selfdestructMutex }; | ||
511 | Lane* lane{ U->selfdestructFirst }; | ||
512 | if (lane != SELFDESTRUCT_END) { | ||
513 | // this causes a leak because we don't call U's destructor (which could be bad if the still running lanes are accessing it) | ||
514 | raise_luaL_error(L_, "Zombie thread %s refuses to die!", lane->debugName); | ||
515 | } | ||
516 | } | ||
517 | |||
518 | // no need to mutex-protect this as all threads in the universe are gone at that point | ||
519 | if (U->timerLinda != nullptr) { // test in case some early internal error prevented Lanes from creating the deep timer | ||
520 | [[maybe_unused]] int const prev_ref_count{ U->timerLinda->refcount.fetch_sub(1, std::memory_order_relaxed) }; | ||
521 | LUA_ASSERT(L_, prev_ref_count == 1); // this should be the last reference | ||
522 | DeepFactory::DeleteDeepObject(L_, U->timerLinda); | ||
523 | U->timerLinda = nullptr; | ||
524 | } | ||
525 | |||
526 | close_keepers(U); | ||
527 | |||
528 | // remove the protected allocator, if any | ||
529 | U->protectedAllocator.removeFrom(L_); | ||
530 | |||
531 | U->Universe::~Universe(); | ||
532 | |||
533 | // universe is no longer available (nor necessary) | ||
534 | // we need to do this in case some deep userdata objects were created before Lanes was initialized, | ||
535 | // as potentially they will be garbage collected after Lanes at application shutdown | ||
536 | universe_store(L_, nullptr); | ||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | // ################################################################################################# | ||
541 | |||
542 | //--- | 441 | //--- |
543 | // = _single( [cores_uint=1] ) | 442 | // = _single( [cores_uint=1] ) |
544 | // | 443 | // |
@@ -1526,14 +1425,13 @@ LUAG_FUNC(threads) | |||
1526 | Universe* const U{ universe_get(L_) }; | 1425 | Universe* const U{ universe_get(L_) }; |
1527 | 1426 | ||
1528 | // List _all_ still running threads | 1427 | // List _all_ still running threads |
1529 | // | ||
1530 | std::lock_guard<std::mutex> guard{ U->trackingMutex }; | 1428 | std::lock_guard<std::mutex> guard{ U->trackingMutex }; |
1531 | if (U->trackingFirst && U->trackingFirst != TRACKING_END) { | 1429 | if (U->trackingFirst && U->trackingFirst != TRACKING_END) { |
1532 | Lane* lane{ U->trackingFirst }; | 1430 | Lane* lane{ U->trackingFirst }; |
1533 | int index{ 0 }; | 1431 | int index{ 0 }; |
1534 | lua_newtable(L_); // L_: {} | 1432 | lua_newtable(L_); // L_: {} |
1535 | while (lane != TRACKING_END) { | 1433 | while (lane != TRACKING_END) { |
1536 | // insert a { name, status } tuple, so that several lanes with the same name can't clobber each other | 1434 | // insert a { name='<name>', status='<status>' } tuple, so that several lanes with the same name can't clobber each other |
1537 | lua_createtable(L_, 0, 2); // L_: {} {} | 1435 | lua_createtable(L_, 0, 2); // L_: {} {} |
1538 | lua_pushstring(L_, lane->debugName); // L_: {} {} "name" | 1436 | lua_pushstring(L_, lane->debugName); // L_: {} {} "name" |
1539 | lua_setfield(L_, -2, "name"); // L_: {} {} | 1437 | lua_setfield(L_, -2, "name"); // L_: {} {} |
@@ -1622,17 +1520,20 @@ LUAG_FUNC(wakeup_conv) | |||
1622 | // ################################################################################################# | 1520 | // ################################################################################################# |
1623 | 1521 | ||
1624 | extern int LG_linda(lua_State* L_); | 1522 | extern int LG_linda(lua_State* L_); |
1625 | static struct luaL_Reg const lanes_functions[] = { | 1523 | |
1626 | { "linda", LG_linda }, | 1524 | namespace global { |
1627 | { "now_secs", LG_now_secs }, | 1525 | static struct luaL_Reg const sLanesFunctions[] = { |
1628 | { "wakeup_conv", LG_wakeup_conv }, | 1526 | { "linda", LG_linda }, |
1629 | { "set_thread_priority", LG_set_thread_priority }, | 1527 | { "now_secs", LG_now_secs }, |
1630 | { "set_thread_affinity", LG_set_thread_affinity }, | 1528 | { "wakeup_conv", LG_wakeup_conv }, |
1631 | { "nameof", luaG_nameof }, | 1529 | { "set_thread_priority", LG_set_thread_priority }, |
1632 | { "register", LG_register }, | 1530 | { "set_thread_affinity", LG_set_thread_affinity }, |
1633 | { "set_singlethreaded", LG_set_singlethreaded }, | 1531 | { "nameof", luaG_nameof }, |
1634 | { nullptr, nullptr } | 1532 | { "register", LG_register }, |
1635 | }; | 1533 | { "set_singlethreaded", LG_set_singlethreaded }, |
1534 | { nullptr, nullptr } | ||
1535 | }; | ||
1536 | } // namespace global | ||
1636 | 1537 | ||
1637 | // ################################################################################################# | 1538 | // ################################################################################################# |
1638 | 1539 | ||
@@ -1715,7 +1616,7 @@ LUAG_FUNC(configure) | |||
1715 | lua_pushnil(L_); // L_: settings M nil | 1616 | lua_pushnil(L_); // L_: settings M nil |
1716 | lua_setfield(L_, -2, "configure"); // L_: settings M | 1617 | lua_setfield(L_, -2, "configure"); // L_: settings M |
1717 | // add functions to the module's table | 1618 | // add functions to the module's table |
1718 | luaG_registerlibfuncs(L_, lanes_functions); | 1619 | luaG_registerlibfuncs(L_, global::sLanesFunctions); |
1719 | #if HAVE_LANE_TRACKING() | 1620 | #if HAVE_LANE_TRACKING() |
1720 | // register core.threads() only if settings say it should be available | 1621 | // register core.threads() only if settings say it should be available |
1721 | if (U->trackingFirst != nullptr) { | 1622 | if (U->trackingFirst != nullptr) { |
@@ -1739,7 +1640,7 @@ LUAG_FUNC(configure) | |||
1739 | // prepare the metatable for threads | 1640 | // prepare the metatable for threads |
1740 | // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname } | 1641 | // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname } |
1741 | // | 1642 | // |
1742 | if (luaL_newmetatable(L_, "Lane")) { // L_: settings M mt | 1643 | if (luaL_newmetatable(L_, kLaneMetatableName)) { // L_: settings M mt |
1743 | lua_pushcfunction(L_, lane_gc); // L_: settings M mt lane_gc | 1644 | lua_pushcfunction(L_, lane_gc); // L_: settings M mt lane_gc |
1744 | lua_setfield(L_, -2, "__gc"); // L_: settings M mt | 1645 | lua_setfield(L_, -2, "__gc"); // L_: settings M mt |
1745 | lua_pushcfunction(L_, LG_thread_index); // L_: settings M mt LG_thread_index | 1646 | lua_pushcfunction(L_, LG_thread_index); // L_: settings M mt LG_thread_index |
@@ -1756,7 +1657,7 @@ LUAG_FUNC(configure) | |||
1756 | lua_setfield(L_, -2, "get_debug_threadname"); // L_: settings M mt | 1657 | lua_setfield(L_, -2, "get_debug_threadname"); // L_: settings M mt |
1757 | lua_pushcfunction(L_, LG_thread_cancel); // L_: settings M mt LG_thread_cancel | 1658 | lua_pushcfunction(L_, LG_thread_cancel); // L_: settings M mt LG_thread_cancel |
1758 | lua_setfield(L_, -2, "cancel"); // L_: settings M mt | 1659 | lua_setfield(L_, -2, "cancel"); // L_: settings M mt |
1759 | lua_pushliteral(L_, "Lane"); // L_: settings M mt "Lane" | 1660 | lua_pushliteral(L_, kLaneMetatableName); // L_: settings M mt "Lane" |
1760 | lua_setfield(L_, -2, "__metatable"); // L_: settings M mt | 1661 | lua_setfield(L_, -2, "__metatable"); // L_: settings M mt |
1761 | } | 1662 | } |
1762 | 1663 | ||