aboutsummaryrefslogtreecommitdiff
path: root/src/lanes.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lanes.cpp')
-rw-r--r--src/lanes.cpp135
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
1624extern int LG_linda(lua_State* L_); 1522extern int LG_linda(lua_State* L_);
1625static struct luaL_Reg const lanes_functions[] = { 1523
1626 { "linda", LG_linda }, 1524namespace 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