aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-05-14 17:29:39 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-05-14 17:29:39 +0200
commitf9f4c841e77ea36797a2ea06cd8859581dfbef94 (patch)
tree947223403341ed2fe3fbf1fcde7a0d43f6647090 /src
parent53eef99942ecca7b1dc43ec6b35d09a5fe6b1b1b (diff)
downloadlanes-f9f4c841e77ea36797a2ea06cd8859581dfbef94.tar.gz
lanes-f9f4c841e77ea36797a2ea06cd8859581dfbef94.tar.bz2
lanes-f9f4c841e77ea36797a2ea06cd8859581dfbef94.zip
Shuffling code around
Diffstat (limited to 'src')
-rw-r--r--src/cancel.cpp1
-rw-r--r--src/debugspew.h48
-rw-r--r--src/intercopycontext.cpp1
-rw-r--r--src/keeper.cpp147
-rw-r--r--src/keeper.h5
-rw-r--r--src/lane.cpp1
-rw-r--r--src/lanes.cpp84
-rw-r--r--src/lanesconf.h2
-rw-r--r--src/linda.cpp176
-rw-r--r--src/macros_and_utils.h15
-rw-r--r--src/state.cpp10
-rw-r--r--src/state.h5
-rw-r--r--src/tools.cpp1
-rw-r--r--src/universe.cpp222
-rw-r--r--src/universe.h36
15 files changed, 388 insertions, 366 deletions
diff --git a/src/cancel.cpp b/src/cancel.cpp
index d0a1349..ff7af1e 100644
--- a/src/cancel.cpp
+++ b/src/cancel.cpp
@@ -35,6 +35,7 @@ THE SOFTWARE.
35 35
36#include "cancel.h" 36#include "cancel.h"
37 37
38#include "debugspew.h"
38#include "lane.h" 39#include "lane.h"
39 40
40// ################################################################################################# 41// #################################################################################################
diff --git a/src/debugspew.h b/src/debugspew.h
new file mode 100644
index 0000000..0c3e14a
--- /dev/null
+++ b/src/debugspew.h
@@ -0,0 +1,48 @@
1#pragma once
2
3#include "lanesconf.h"
4#include "universe.h"
5
6// #################################################################################################
7
8#if USE_DEBUG_SPEW()
9
10class DebugSpewIndentScope
11{
12 private:
13 Universe* const U;
14
15 public:
16 static char const* const debugspew_indent;
17
18 DebugSpewIndentScope(Universe* U_)
19 : U{ U_ }
20 {
21 if (U)
22 U->debugspewIndentDepth.fetch_add(1, std::memory_order_relaxed);
23 }
24
25 ~DebugSpewIndentScope()
26 {
27 if (U)
28 U->debugspewIndentDepth.fetch_sub(1, std::memory_order_relaxed);
29 }
30};
31
32// #################################################################################################
33
34#define INDENT_BEGIN "%.*s "
35#define INDENT_END(U_) , (U_ ? U_->debugspewIndentDepth.load(std::memory_order_relaxed) : 0), DebugSpewIndentScope::debugspew_indent
36#define DEBUGSPEW_CODE(_code) _code
37#define DEBUGSPEW_OR_NOT(a_, b_) a_
38#define DEBUGSPEW_PARAM_COMMA(param_) param_,
39#define DEBUGSPEW_COMMA_PARAM(param_) , param_
40
41#else // USE_DEBUG_SPEW()
42
43#define DEBUGSPEW_CODE(_code)
44#define DEBUGSPEW_OR_NOT(a_, b_) b_
45#define DEBUGSPEW_PARAM_COMMA(param_)
46#define DEBUGSPEW_COMMA_PARAM(param_)
47
48#endif // USE_DEBUG_SPEW()
diff --git a/src/intercopycontext.cpp b/src/intercopycontext.cpp
index 6e5d1d8..6a26018 100644
--- a/src/intercopycontext.cpp
+++ b/src/intercopycontext.cpp
@@ -26,6 +26,7 @@ THE SOFTWARE.
26 26
27#include "intercopycontext.h" 27#include "intercopycontext.h"
28 28
29#include "debugspew.h"
29#include "deep.h" 30#include "deep.h"
30#include "universe.h" 31#include "universe.h"
31 32
diff --git a/src/keeper.cpp b/src/keeper.cpp
index dcfa2ec..dcfb431 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -545,146 +545,6 @@ int keepercall_count(lua_State* L_)
545// Keeper API, accessed from linda methods 545// Keeper API, accessed from linda methods
546// ################################################################################################# 546// #################################################################################################
547 547
548/*
549 * Pool of keeper states
550 *
551 * Access to keeper states is locked (only one OS thread at a time) so the
552 * bigger the pool, the less chances of unnecessary waits. Lindas map to the
553 * keepers randomly, by a hash.
554 */
555
556// called as __gc for the keepers array userdata
557void close_keepers(Universe* U_)
558{
559 if (U_->keepers != nullptr) {
560 int _nbKeepers{ U_->keepers->nb_keepers };
561 // NOTE: imagine some keeper state N+1 currently holds a linda that uses another keeper N, and a _gc that will make use of it
562 // when keeper N+1 is closed, object is GCed, linda operation is called, which attempts to acquire keeper N, whose Lua state no longer exists
563 // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success
564 // which is early-outed with a U->keepers->nbKeepers null-check
565 U_->keepers->nb_keepers = 0;
566 for (int _i = 0; _i < _nbKeepers; ++_i) {
567 lua_State* const K{ U_->keepers->keeper_array[_i].L };
568 U_->keepers->keeper_array[_i].L = KeeperState{ nullptr };
569 if (K != nullptr) {
570 lua_close(K);
571 } else {
572 // detected partial init: destroy only the mutexes that got initialized properly
573 _nbKeepers = _i;
574 }
575 }
576 for (int _i = 0; _i < _nbKeepers; ++_i) {
577 U_->keepers->keeper_array[_i].~Keeper();
578 }
579 // free the keeper bookkeeping structure
580 U_->internalAllocator.free(U_->keepers, sizeof(Keepers) + (_nbKeepers - 1) * sizeof(Keeper));
581 U_->keepers = nullptr;
582 }
583}
584
585// #################################################################################################
586
587/*
588 * Initialize keeper states
589 *
590 * If there is a problem, returns nullptr and pushes the error message on the stack
591 * else returns the keepers bookkeeping structure.
592 *
593 * Note: Any problems would be design flaws; the created Lua state is left
594 * unclosed, because it does not really matter. In production code, this
595 * function never fails.
596 * settings table is expected at position 1 on the stack
597 */
598void init_keepers(Universe* U_, lua_State* L_)
599{
600 LUA_ASSERT(L_, lua_gettop(L_) == 1 && lua_istable(L_, 1));
601 STACK_CHECK_START_REL(L_, 0); // L_: settings
602 lua_getfield(L_, 1, "nb_keepers"); // L_: settings nb_keepers
603 int const _nb_keepers{ static_cast<int>(lua_tointeger(L_, -1)) };
604 lua_pop(L_, 1); // L_: settings
605 if (_nb_keepers < 1) {
606 raise_luaL_error(L_, "Bad number of keepers (%d)", _nb_keepers);
607 }
608 STACK_CHECK(L_, 0);
609
610 lua_getfield(L_, 1, "keepers_gc_threshold"); // L_: settings keepers_gc_threshold
611 int const keepers_gc_threshold{ static_cast<int>(lua_tointeger(L_, -1)) };
612 lua_pop(L_, 1); // L_: settings
613 STACK_CHECK(L_, 0);
614
615 // Keepers contains an array of 1 Keeper, adjust for the actual number of keeper states
616 {
617 size_t const bytes = sizeof(Keepers) + (_nb_keepers - 1) * sizeof(Keeper);
618 U_->keepers = static_cast<Keepers*>(U_->internalAllocator.alloc(bytes));
619 if (U_->keepers == nullptr) {
620 raise_luaL_error(L_, "init_keepers() failed while creating keeper array; out of memory");
621 }
622 U_->keepers->Keepers::Keepers();
623 U_->keepers->gc_threshold = keepers_gc_threshold;
624 U_->keepers->nb_keepers = _nb_keepers;
625
626 for (int _i = 0; _i < _nb_keepers; ++_i) {
627 U_->keepers->keeper_array[_i].Keeper::Keeper();
628 }
629 }
630 for (int _i = 0; _i < _nb_keepers; ++_i) {
631 // note that we will leak K if we raise an error later
632 KeeperState const _K{ create_state(U_, L_) }; // L_: settings K:
633 if (_K == nullptr) {
634 raise_luaL_error(L_, "init_keepers() failed while creating keeper states; out of memory");
635 }
636
637 U_->keepers->keeper_array[_i].L = _K;
638
639 if (U_->keepers->gc_threshold >= 0) {
640 lua_gc(_K, LUA_GCSTOP, 0);
641 }
642
643 STACK_CHECK_START_ABS(_K, 0);
644
645 // copy the universe pointer in the keeper itself
646 universe_store(_K, U_);
647 STACK_CHECK(_K, 0);
648
649 // make sure 'package' is initialized in keeper states, so that we have require()
650 // this because this is needed when transferring deep userdata object
651 luaL_requiref(_K, LUA_LOADLIBNAME, luaopen_package, 1); // L_: settings K: package
652 lua_pop(_K, 1); // L_: settings K:
653 STACK_CHECK(_K, 0);
654 serialize_require(DEBUGSPEW_PARAM_COMMA(U_) _K);
655 STACK_CHECK(_K, 0);
656
657 // copy package.path and package.cpath from the source state
658 if (luaG_getmodule(L_, LUA_LOADLIBNAME) != LuaType::NIL) { // L_: settings package K:
659 // when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately
660 InterCopyContext _c{ U_, DestState{ _K }, SourceState{ L_ }, {}, SourceIndex{ lua_absindex(L_, -1) }, {}, LookupMode::ToKeeper, {} };
661 if (_c.inter_copy_package() != InterCopyResult::Success) { // L_: settings ... error_msg K:
662 // if something went wrong, the error message is at the top of the stack
663 lua_remove(L_, -2); // L_: settings error_msg
664 raise_lua_error(L_);
665 }
666 }
667 lua_pop(L_, 1); // L_: settings K:
668 STACK_CHECK(L_, 0);
669 STACK_CHECK(_K, 0);
670
671 // attempt to call on_state_create(), if we have one and it is a C function
672 // (only support a C function because we can't transfer executable Lua code in keepers)
673 // will raise an error in L_ in case of problem
674 callOnStateCreate(U_, _K, L_, LookupMode::ToKeeper);
675
676 // to see VM name in Decoda debugger
677 lua_pushfstring(_K, "Keeper #%d", _i + 1); // L_: settings K: "Keeper #n"
678 lua_setglobal(_K, "decoda_name"); // L_: settings K:
679 // create the fifos table in the keeper state
680 kFifosRegKey.setValue(_K, [](lua_State* L_) { lua_newtable(L_); }); // L_: settings K:
681 STACK_CHECK(_K, 0);
682 }
683 STACK_CHECK(L_, 0);
684}
685
686// #################################################################################################
687
688Keeper* Linda::acquireKeeper() const 548Keeper* Linda::acquireKeeper() const
689{ 549{
690 int const _nbKeepers{ U->keepers->nb_keepers }; 550 int const _nbKeepers{ U->keepers->nb_keepers };
@@ -789,3 +649,10 @@ KeeperCallResult keeper_call(KeeperState K_, keeper_api_t func_, lua_State* L_,
789 649
790 return _result; 650 return _result;
791} 651}
652
653// #################################################################################################
654
655void Keepers::CreateFifosTable(lua_State* L_)
656{
657 kFifosRegKey.setValue(L_, [](lua_State* L_) { lua_newtable(L_); });
658}
diff --git a/src/keeper.h b/src/keeper.h
index ebe2946..62d9ec8 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -33,14 +33,13 @@ struct Keepers
33 int gc_threshold{ 0 }; 33 int gc_threshold{ 0 };
34 int nb_keepers{ 0 }; 34 int nb_keepers{ 0 };
35 Keeper keeper_array[1]; 35 Keeper keeper_array[1];
36
37 static void CreateFifosTable(lua_State* L_);
36}; 38};
37 39
38// xxh64 of string "kNilSentinel" generated at https://www.pelock.com/products/hash-calculator 40// xxh64 of string "kNilSentinel" generated at https://www.pelock.com/products/hash-calculator
39static constexpr UniqueKey kNilSentinel{ 0xC457D4EDDB05B5E4ull, "lanes.null" }; 41static constexpr UniqueKey kNilSentinel{ 0xC457D4EDDB05B5E4ull, "lanes.null" };
40 42
41void init_keepers(Universe* U_, lua_State* L_);
42void close_keepers(Universe* U_);
43
44void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mode_); 43void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mode_);
45[[nodiscard]] int keeper_push_linda_storage(Linda& linda_, DestState L_); 44[[nodiscard]] int keeper_push_linda_storage(Linda& linda_, DestState L_);
46 45
diff --git a/src/lane.cpp b/src/lane.cpp
index bc82291..4c33afb 100644
--- a/src/lane.cpp
+++ b/src/lane.cpp
@@ -26,6 +26,7 @@ THE SOFTWARE.
26 26
27#include "lane.h" 27#include "lane.h"
28 28
29#include "debugspew.h"
29#include "intercopycontext.h" 30#include "intercopycontext.h"
30#include "tools.h" 31#include "tools.h"
31#include "threading.h" 32#include "threading.h"
diff --git a/src/lanes.cpp b/src/lanes.cpp
index 6a84422..b3f7be7 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -576,84 +576,6 @@ LUAG_FUNC(wakeup_conv)
576} 576}
577 577
578// ################################################################################################# 578// #################################################################################################
579// ################################### custom allocator support ####################################
580// #################################################################################################
581
582// same as PUC-Lua l_alloc
583extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud_, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_)
584{
585 if (nsize_ == 0) {
586 free(ptr_);
587 return nullptr;
588 } else {
589 return realloc(ptr_, nsize_);
590 }
591}
592
593// #################################################################################################
594
595[[nodiscard]] static int luaG_provide_protected_allocator(lua_State* L_)
596{
597 Universe* const _U{ universe_get(L_) };
598 // push a new full userdata on the stack, giving access to the universe's protected allocator
599 [[maybe_unused]] AllocatorDefinition* const def{ new (L_) AllocatorDefinition{ _U->protectedAllocator.makeDefinition() } };
600 return 1;
601}
602
603// #################################################################################################
604
605// called once at the creation of the universe (therefore L is the master Lua state everything originates from)
606// Do I need to disable this when compiling for LuaJIT to prevent issues?
607static void initialize_allocator_function(Universe* U_, lua_State* L_)
608{
609 STACK_CHECK_START_REL(L_, 1); // L_: settings
610 lua_getfield(L_, -1, "allocator"); // L_: settings allocator|nil|"protected"
611 if (!lua_isnil(L_, -1)) {
612 // store C function pointer in an internal variable
613 U_->provideAllocator = lua_tocfunction(L_, -1); // L_: settings allocator
614 if (U_->provideAllocator != nullptr) {
615 // make sure the function doesn't have upvalues
616 char const* upname = lua_getupvalue(L_, -1, 1); // L_: settings allocator upval?
617 if (upname != nullptr) { // should be "" for C functions with upvalues if any
618 raise_luaL_error(L_, "config.allocator() shouldn't have upvalues");
619 }
620 // remove this C function from the config table so that it doesn't cause problems
621 // when we transfer the config table in newly created Lua states
622 lua_pushnil(L_); // L_: settings allocator nil
623 lua_setfield(L_, -3, "allocator"); // L_: settings allocator
624 } else if (lua_type(L_, -1) == LUA_TSTRING) { // should be "protected"
625 LUA_ASSERT(L_, strcmp(lua_tostring(L_, -1), "protected") == 0);
626 // set the original allocator to call from inside protection by the mutex
627 U_->protectedAllocator.initFrom(L_);
628 U_->protectedAllocator.installIn(L_);
629 // before a state is created, this function will be called to obtain the allocator
630 U_->provideAllocator = luaG_provide_protected_allocator;
631 }
632 } else {
633 // just grab whatever allocator was provided to lua_newstate
634 U_->protectedAllocator.initFrom(L_);
635 }
636 lua_pop(L_, 1); // L_: settings
637 STACK_CHECK(L_, 1);
638
639 lua_getfield(L_, -1, "internal_allocator"); // L_: settings "libc"|"allocator"
640 {
641 char const* const _allocator{ lua_tostring(L_, -1) };
642 if (strcmp(_allocator, "libc") == 0) {
643 U_->internalAllocator = AllocatorDefinition{ libc_lua_Alloc, nullptr };
644 } else if (U_->provideAllocator == luaG_provide_protected_allocator) {
645 // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case.
646 U_->internalAllocator = U_->protectedAllocator.makeDefinition();
647 } else {
648 // no protection required, just use whatever we have as-is.
649 U_->internalAllocator = U_->protectedAllocator;
650 }
651 }
652 lua_pop(L_, 1); // L_: settings
653 STACK_CHECK(L_, 1);
654}
655
656// #################################################################################################
657// ######################################## Module linkage ######################################### 579// ######################################## Module linkage #########################################
658// ################################################################################################# 580// #################################################################################################
659 581
@@ -728,9 +650,9 @@ LUAG_FUNC(configure)
728#endif // HAVE_LANE_TRACKING() 650#endif // HAVE_LANE_TRACKING()
729 // Linked chains handling 651 // Linked chains handling
730 _U->selfdestructFirst = SELFDESTRUCT_END; 652 _U->selfdestructFirst = SELFDESTRUCT_END;
731 initialize_allocator_function(_U, L_); 653 _U->initializeAllocatorFunction(L_);
732 initializeOnStateCreate(_U, L_); 654 InitializeOnStateCreate(_U, L_);
733 init_keepers(_U, L_); 655 _U->initializeKeepers(L_);
734 STACK_CHECK(L_, 1); 656 STACK_CHECK(L_, 1);
735 657
736 // Initialize 'timerLinda'; a common Linda object shared by all states 658 // Initialize 'timerLinda'; a common Linda object shared by all states
diff --git a/src/lanesconf.h b/src/lanesconf.h
index aae4f5b..b35becc 100644
--- a/src/lanesconf.h
+++ b/src/lanesconf.h
@@ -39,3 +39,5 @@
39#define LANES_API extern 39#define LANES_API extern
40#endif // __cplusplus 40#endif // __cplusplus
41#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 41#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
42
43#define USE_DEBUG_SPEW() 0
diff --git a/src/linda.cpp b/src/linda.cpp
index ad40eef..a7cf011 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -39,6 +39,72 @@ THE SOFTWARE.
39#include <functional> 39#include <functional>
40 40
41// ################################################################################################# 41// #################################################################################################
42
43static void check_key_types(lua_State* L_, int start_, int end_)
44{
45 for (int _i{ start_ }; _i <= end_; ++_i) {
46 LuaType const t{ lua_type_as_enum(L_, _i) };
47 switch (t) {
48 case LuaType::BOOLEAN:
49 case LuaType::NUMBER:
50 case LuaType::STRING:
51 continue;
52
53 case LuaType::LIGHTUSERDATA:
54 static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel };
55 for (UniqueKey const& key : kKeysToCheck) {
56 if (key.equals(L_, _i)) {
57 raise_luaL_error(L_, "argument #%d: can't use %s as a key", _i, key.debugName);
58 break;
59 }
60 }
61 break;
62 }
63 raise_luaL_error(L_, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", _i);
64 }
65}
66
67// #################################################################################################
68
69/*
70 * string = linda:__tostring( linda_ud)
71 *
72 * Return the stringification of a linda
73 *
74 * Useful for concatenation or debugging purposes
75 */
76
77template <bool OPT>
78[[nodiscard]] static int LindaToString(lua_State* L_, int idx_)
79{
80 Linda* const _linda{ ToLinda<OPT>(L_, idx_) };
81 if (_linda != nullptr) {
82 char _text[128];
83 int _len;
84 if (_linda->getName())
85 _len = sprintf(_text, "Linda: %.*s", (int) sizeof(_text) - 8, _linda->getName());
86 else
87 _len = sprintf(_text, "Linda: %p", _linda);
88 lua_pushlstring(L_, _text, _len);
89 return 1;
90 }
91 return 0;
92}
93
94// #################################################################################################
95
96template <bool OPT>
97[[nodiscard]] static inline Linda* ToLinda(lua_State* L_, int idx_)
98{
99 Linda* const _linda{ static_cast<Linda*>(LindaFactory::Instance.toDeep(L_, idx_)) };
100 if constexpr (!OPT) {
101 luaL_argcheck(L_, _linda != nullptr, idx_, "expecting a linda object"); // doesn't return if linda is nullptr
102 LUA_ASSERT(L_, _linda->U == universe_get(L_));
103 }
104 return _linda;
105}
106
107// #################################################################################################
42// ################################################################################################# 108// #################################################################################################
43 109
44// Any hashing will do that maps pointers to [0..Universe::nb_keepers[ consistently. 110// Any hashing will do that maps pointers to [0..Universe::nb_keepers[ consistently.
@@ -66,27 +132,6 @@ Linda::~Linda()
66 132
67// ################################################################################################# 133// #################################################################################################
68 134
69void Linda::setName(char const* name_, size_t len_)
70{
71 // keep default
72 if (!name_ || len_ == 0) {
73 return;
74 }
75 ++len_; // don't forget terminating 0
76 if (len_ < kEmbeddedNameLength) {
77 nameVariant.emplace<EmbeddedName>();
78 char* const _name{ std::get<EmbeddedName>(nameVariant).data() };
79 memcpy(_name, name_, len_);
80 } else {
81 AllocatedName& _name = std::get<AllocatedName>(nameVariant);
82 _name.name = static_cast<char*>(U->internalAllocator.alloc(len_));
83 _name.len = len_;
84 memcpy(_name.name, name_, len_);
85 }
86}
87
88// #################################################################################################
89
90char const* Linda::getName() const 135char const* Linda::getName() const
91{ 136{
92 if (std::holds_alternative<AllocatedName>(nameVariant)) { 137 if (std::holds_alternative<AllocatedName>(nameVariant)) {
@@ -102,45 +147,6 @@ char const* Linda::getName() const
102 147
103// ################################################################################################# 148// #################################################################################################
104 149
105template <bool OPT>
106[[nodiscard]] static inline Linda* ToLinda(lua_State* L_, int idx_)
107{
108 Linda* const _linda{ static_cast<Linda*>(LindaFactory::Instance.toDeep(L_, idx_)) };
109 if constexpr (!OPT) {
110 luaL_argcheck(L_, _linda != nullptr, idx_, "expecting a linda object"); // doesn't return if linda is nullptr
111 LUA_ASSERT(L_, _linda->U == universe_get(L_));
112 }
113 return _linda;
114}
115
116// #################################################################################################
117
118static void check_key_types(lua_State* L_, int start_, int end_)
119{
120 for (int _i{ start_ }; _i <= end_; ++_i) {
121 LuaType const t{ lua_type_as_enum(L_, _i) };
122 switch (t) {
123 case LuaType::BOOLEAN:
124 case LuaType::NUMBER:
125 case LuaType::STRING:
126 continue;
127
128 case LuaType::LIGHTUSERDATA:
129 static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel };
130 for (UniqueKey const& key : kKeysToCheck) {
131 if (key.equals(L_, _i)) {
132 raise_luaL_error(L_, "argument #%d: can't use %s as a key", _i, key.debugName);
133 break;
134 }
135 }
136 break;
137 }
138 raise_luaL_error(L_, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", _i);
139 }
140}
141
142// #################################################################################################
143
144// used to perform all linda operations that access keepers 150// used to perform all linda operations that access keepers
145int Linda::ProtectedCall(lua_State* L_, lua_CFunction f_) 151int Linda::ProtectedCall(lua_State* L_, lua_CFunction f_)
146{ 152{
@@ -175,6 +181,29 @@ int Linda::ProtectedCall(lua_State* L_, lua_CFunction f_)
175 181
176// ################################################################################################# 182// #################################################################################################
177 183
184void Linda::setName(char const* name_, size_t len_)
185{
186 // keep default
187 if (!name_ || len_ == 0) {
188 return;
189 }
190 ++len_; // don't forget terminating 0
191 if (len_ < kEmbeddedNameLength) {
192 nameVariant.emplace<EmbeddedName>();
193 char* const _name{ std::get<EmbeddedName>(nameVariant).data() };
194 memcpy(_name, name_, len_);
195 } else {
196 AllocatedName& _name = std::get<AllocatedName>(nameVariant);
197 _name.name = static_cast<char*>(U->internalAllocator.alloc(len_));
198 _name.len = len_;
199 memcpy(_name.name, name_, len_);
200 }
201}
202
203// #################################################################################################
204// ########################################## Lua API ##############################################
205// #################################################################################################
206
178/* 207/*
179 * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...) 208 * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...)
180 * 209 *
@@ -649,33 +678,6 @@ LUAG_FUNC(linda_deep)
649 678
650// ################################################################################################# 679// #################################################################################################
651 680
652/*
653 * string = linda:__tostring( linda_ud)
654 *
655 * Return the stringification of a linda
656 *
657 * Useful for concatenation or debugging purposes
658 */
659
660template <bool OPT>
661[[nodiscard]] static int LindaToString(lua_State* L_, int idx_)
662{
663 Linda* const _linda{ ToLinda<OPT>(L_, idx_) };
664 if (_linda != nullptr) {
665 char _text[128];
666 int _len;
667 if (_linda->getName())
668 _len = sprintf(_text, "Linda: %.*s", (int) sizeof(_text) - 8, _linda->getName());
669 else
670 _len = sprintf(_text, "Linda: %p", _linda);
671 lua_pushlstring(L_, _text, _len);
672 return 1;
673 }
674 return 0;
675}
676
677// #################################################################################################
678
679LUAG_FUNC(linda_tostring) 681LUAG_FUNC(linda_tostring)
680{ 682{
681 return LindaToString<false>(L_, 1); 683 return LindaToString<false>(L_, 1);
diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h
index 5987e73..d71c9da 100644
--- a/src/macros_and_utils.h
+++ b/src/macros_and_utils.h
@@ -61,21 +61,6 @@ template <typename... ARGS>
61 61
62// ################################################################################################# 62// #################################################################################################
63 63
64#define USE_DEBUG_SPEW() 0
65#if USE_DEBUG_SPEW()
66#define INDENT_BEGIN "%.*s "
67#define INDENT_END(U_) , (U_ ? U_->debugspewIndentDepth.load(std::memory_order_relaxed) : 0), DebugSpewIndentScope::debugspew_indent
68#define DEBUGSPEW_CODE(_code) _code
69#define DEBUGSPEW_OR_NOT(a_, b_) a_
70#define DEBUGSPEW_PARAM_COMMA(param_) param_,
71#define DEBUGSPEW_COMMA_PARAM(param_) , param_
72#else // USE_DEBUG_SPEW()
73#define DEBUGSPEW_CODE(_code)
74#define DEBUGSPEW_OR_NOT(a_, b_) b_
75#define DEBUGSPEW_PARAM_COMMA(param_)
76#define DEBUGSPEW_COMMA_PARAM(param_)
77#endif // USE_DEBUG_SPEW()
78
79#ifdef NDEBUG 64#ifdef NDEBUG
80 65
81#define LUA_ASSERT(L_, c) ; // nothing 66#define LUA_ASSERT(L_, c) ; // nothing
diff --git a/src/state.cpp b/src/state.cpp
index 56ac74b..a6c9859 100644
--- a/src/state.cpp
+++ b/src/state.cpp
@@ -215,7 +215,7 @@ static void copy_one_time_settings(Universe* U_, SourceState L1_, DestState L2_)
215 215
216// ################################################################################################# 216// #################################################################################################
217 217
218void initializeOnStateCreate(Universe* U_, lua_State* L_) 218void InitializeOnStateCreate(Universe* U_, lua_State* L_)
219{ 219{
220 STACK_CHECK_START_REL(L_, 1); // L_: settings 220 STACK_CHECK_START_REL(L_, 1); // L_: settings
221 lua_getfield(L_, -1, "on_state_create"); // L_: settings on_state_create|nil 221 lua_getfield(L_, -1, "on_state_create"); // L_: settings on_state_create|nil
@@ -234,7 +234,7 @@ void initializeOnStateCreate(Universe* U_, lua_State* L_)
234 lua_setfield(L_, -3, "on_state_create"); // L_: settings on_state_create 234 lua_setfield(L_, -3, "on_state_create"); // L_: settings on_state_create
235 } else { 235 } else {
236 // optim: store marker saying we have such a function in the config table 236 // optim: store marker saying we have such a function in the config table
237 U_->onStateCreateFunc = reinterpret_cast<lua_CFunction>(initializeOnStateCreate); 237 U_->onStateCreateFunc = reinterpret_cast<lua_CFunction>(InitializeOnStateCreate);
238 } 238 }
239 } 239 }
240 lua_pop(L_, 1); // L_: settings 240 lua_pop(L_, 1); // L_: settings
@@ -272,12 +272,12 @@ lua_State* create_state([[maybe_unused]] Universe* U_, lua_State* from_)
272 272
273// ################################################################################################# 273// #################################################################################################
274 274
275void callOnStateCreate(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_) 275void CallOnStateCreate(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_)
276{ 276{
277 if (U_->onStateCreateFunc != nullptr) { 277 if (U_->onStateCreateFunc != nullptr) {
278 STACK_CHECK_START_REL(L_, 0); 278 STACK_CHECK_START_REL(L_, 0);
279 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END(U_))); 279 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END(U_)));
280 if (U_->onStateCreateFunc != reinterpret_cast<lua_CFunction>(initializeOnStateCreate)) { 280 if (U_->onStateCreateFunc != reinterpret_cast<lua_CFunction>(InitializeOnStateCreate)) {
281 // C function: recreate a closure in the new state, bypassing the lookup scheme 281 // C function: recreate a closure in the new state, bypassing the lookup scheme
282 lua_pushcfunction(L_, U_->onStateCreateFunc); // on_state_create() 282 lua_pushcfunction(L_, U_->onStateCreateFunc); // on_state_create()
283 } else { // Lua function located in the config table, copied when we opened "lanes.core" 283 } else { // Lua function located in the config table, copied when we opened "lanes.core"
@@ -393,7 +393,7 @@ lua_State* luaG_newstate(Universe* U_, SourceState from_, char const* libs_)
393 393
394 // call this after the base libraries are loaded and GC is restarted 394 // call this after the base libraries are loaded and GC is restarted
395 // will raise an error in from_ in case of problem 395 // will raise an error in from_ in case of problem
396 callOnStateCreate(U_, _L, from_, LookupMode::LaneBody); 396 CallOnStateCreate(U_, _L, from_, LookupMode::LaneBody);
397 397
398 STACK_CHECK(_L, 0); 398 STACK_CHECK(_L, 0);
399 // after all this, register everything we find in our name<->function database 399 // after all this, register everything we find in our name<->function database
diff --git a/src/state.h b/src/state.h
index 1b25736..626a696 100644
--- a/src/state.h
+++ b/src/state.h
@@ -1,5 +1,6 @@
1#pragma once 1#pragma once
2 2
3#include "debugspew.h"
3#include "macros_and_utils.h" 4#include "macros_and_utils.h"
4 5
5// forwards 6// forwards
@@ -15,5 +16,5 @@ void serialize_require(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L_);
15 16
16// ################################################################################################# 17// #################################################################################################
17 18
18void initializeOnStateCreate(Universe* U_, lua_State* L_); 19void InitializeOnStateCreate(Universe* U_, lua_State* L_);
19void callOnStateCreate(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_); 20void CallOnStateCreate(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_);
diff --git a/src/tools.cpp b/src/tools.cpp
index a70fdc9..eff865c 100644
--- a/src/tools.cpp
+++ b/src/tools.cpp
@@ -34,6 +34,7 @@ THE SOFTWARE.
34 34
35#include "tools.h" 35#include "tools.h"
36 36
37#include "debugspew.h"
37#include "universe.h" 38#include "universe.h"
38 39
39DEBUGSPEW_CODE(char const* const DebugSpewIndentScope::debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+"); 40DEBUGSPEW_CODE(char const* const DebugSpewIndentScope::debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+");
diff --git a/src/universe.cpp b/src/universe.cpp
index dd293f2..9ab1290 100644
--- a/src/universe.cpp
+++ b/src/universe.cpp
@@ -31,8 +31,10 @@ THE SOFTWARE.
31#include "universe.h" 31#include "universe.h"
32 32
33#include "deep.h" 33#include "deep.h"
34#include "intercopycontext.h"
34#include "keeper.h" 35#include "keeper.h"
35#include "lane.h" 36#include "lane.h"
37#include "state.h"
36 38
37// ################################################################################################# 39// #################################################################################################
38 40
@@ -78,6 +80,224 @@ Universe::Universe()
78} 80}
79 81
80// ################################################################################################# 82// #################################################################################################
83// ################################### custom allocator support ####################################
84// #################################################################################################
85
86// same as PUC-Lua l_alloc
87extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud_, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_)
88{
89 if (nsize_ == 0) {
90 free(ptr_);
91 return nullptr;
92 } else {
93 return realloc(ptr_, nsize_);
94 }
95}
96
97// #################################################################################################
98
99[[nodiscard]] static int luaG_provide_protected_allocator(lua_State* L_)
100{
101 Universe* const _U{ universe_get(L_) };
102 // push a new full userdata on the stack, giving access to the universe's protected allocator
103 [[maybe_unused]] AllocatorDefinition* const def{ new (L_) AllocatorDefinition{ _U->protectedAllocator.makeDefinition() } };
104 return 1;
105}
106
107// #################################################################################################
108
109/*
110 * Pool of keeper states
111 *
112 * Access to keeper states is locked (only one OS thread at a time) so the
113 * bigger the pool, the less chances of unnecessary waits. Lindas map to the
114 * keepers randomly, by a hash.
115 */
116
117// called as __gc for the keepers array userdata
118void Universe::closeKeepers()
119{
120 if (keepers != nullptr) {
121 int _nbKeepers{ keepers->nb_keepers };
122 // NOTE: imagine some keeper state N+1 currently holds a linda that uses another keeper N, and a _gc that will make use of it
123 // when keeper N+1 is closed, object is GCed, linda operation is called, which attempts to acquire keeper N, whose Lua state no longer exists
124 // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success
125 // which is early-outed with a keepers->nbKeepers null-check
126 keepers->nb_keepers = 0;
127 for (int _i = 0; _i < _nbKeepers; ++_i) {
128 lua_State* const _K{ keepers->keeper_array[_i].L };
129 keepers->keeper_array[_i].L = KeeperState{ nullptr };
130 if (_K != nullptr) {
131 lua_close(_K);
132 } else {
133 // detected partial init: destroy only the mutexes that got initialized properly
134 _nbKeepers = _i;
135 }
136 }
137 for (int _i = 0; _i < _nbKeepers; ++_i) {
138 keepers->keeper_array[_i].~Keeper();
139 }
140 // free the keeper bookkeeping structure
141 internalAllocator.free(keepers, sizeof(Keepers) + (_nbKeepers - 1) * sizeof(Keeper));
142 keepers = nullptr;
143 }
144}
145
146// #################################################################################################
147
148// called once at the creation of the universe (therefore L_ is the master Lua state everything originates from)
149// Do I need to disable this when compiling for LuaJIT to prevent issues?
150void Universe::initializeAllocatorFunction(lua_State* L_)
151{
152 STACK_CHECK_START_REL(L_, 1); // L_: settings
153 lua_getfield(L_, -1, "allocator"); // L_: settings allocator|nil|"protected"
154 if (!lua_isnil(L_, -1)) {
155 // store C function pointer in an internal variable
156 provideAllocator = lua_tocfunction(L_, -1); // L_: settings allocator
157 if (provideAllocator != nullptr) {
158 // make sure the function doesn't have upvalues
159 char const* upname = lua_getupvalue(L_, -1, 1); // L_: settings allocator upval?
160 if (upname != nullptr) { // should be "" for C functions with upvalues if any
161 raise_luaL_error(L_, "config.allocator() shouldn't have upvalues");
162 }
163 // remove this C function from the config table so that it doesn't cause problems
164 // when we transfer the config table in newly created Lua states
165 lua_pushnil(L_); // L_: settings allocator nil
166 lua_setfield(L_, -3, "allocator"); // L_: settings allocator
167 } else if (lua_type(L_, -1) == LUA_TSTRING) { // should be "protected"
168 LUA_ASSERT(L_, strcmp(lua_tostring(L_, -1), "protected") == 0);
169 // set the original allocator to call from inside protection by the mutex
170 protectedAllocator.initFrom(L_);
171 protectedAllocator.installIn(L_);
172 // before a state is created, this function will be called to obtain the allocator
173 provideAllocator = luaG_provide_protected_allocator;
174 }
175 } else {
176 // just grab whatever allocator was provided to lua_newstate
177 protectedAllocator.initFrom(L_);
178 }
179 lua_pop(L_, 1); // L_: settings
180 STACK_CHECK(L_, 1);
181
182 lua_getfield(L_, -1, "internal_allocator"); // L_: settings "libc"|"allocator"
183 {
184 char const* const _allocator{ lua_tostring(L_, -1) };
185 if (strcmp(_allocator, "libc") == 0) {
186 internalAllocator = AllocatorDefinition{ libc_lua_Alloc, nullptr };
187 } else if (provideAllocator == luaG_provide_protected_allocator) {
188 // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case.
189 internalAllocator = protectedAllocator.makeDefinition();
190 } else {
191 // no protection required, just use whatever we have as-is.
192 internalAllocator = protectedAllocator;
193 }
194 }
195 lua_pop(L_, 1); // L_: settings
196 STACK_CHECK(L_, 1);
197}
198
199// #################################################################################################
200
201/*
202 * Initialize keeper states
203 *
204 * If there is a problem, returns nullptr and pushes the error message on the stack
205 * else returns the keepers bookkeeping structure.
206 *
207 * Note: Any problems would be design flaws; the created Lua state is left
208 * unclosed, because it does not really matter. In production code, this
209 * function never fails.
210 * settings table is expected at position 1 on the stack
211 */
212void Universe::initializeKeepers(lua_State* L_)
213{
214 LUA_ASSERT(L_, lua_gettop(L_) == 1 && lua_istable(L_, 1));
215 STACK_CHECK_START_REL(L_, 0); // L_: settings
216 lua_getfield(L_, 1, "nb_keepers"); // L_: settings nb_keepers
217 int const _nb_keepers{ static_cast<int>(lua_tointeger(L_, -1)) };
218 lua_pop(L_, 1); // L_: settings
219 if (_nb_keepers < 1) {
220 raise_luaL_error(L_, "Bad number of keepers (%d)", _nb_keepers);
221 }
222 STACK_CHECK(L_, 0);
223
224 lua_getfield(L_, 1, "keepers_gc_threshold"); // L_: settings keepers_gc_threshold
225 int const keepers_gc_threshold{ static_cast<int>(lua_tointeger(L_, -1)) };
226 lua_pop(L_, 1); // L_: settings
227 STACK_CHECK(L_, 0);
228
229 // Keepers contains an array of 1 Keeper, adjust for the actual number of keeper states
230 {
231 size_t const bytes = sizeof(Keepers) + (_nb_keepers - 1) * sizeof(Keeper);
232 keepers = static_cast<Keepers*>(internalAllocator.alloc(bytes));
233 if (keepers == nullptr) {
234 raise_luaL_error(L_, "out of memory while creating keepers");
235 }
236 keepers->Keepers::Keepers();
237 keepers->gc_threshold = keepers_gc_threshold;
238 keepers->nb_keepers = _nb_keepers;
239
240 for (int _i = 0; _i < _nb_keepers; ++_i) {
241 keepers->keeper_array[_i].Keeper::Keeper();
242 }
243 }
244 for (int _i = 0; _i < _nb_keepers; ++_i) {
245 // note that we will leak K if we raise an error later
246 KeeperState const _K{ create_state(this, L_) }; // L_: settings K:
247 if (_K == nullptr) {
248 raise_luaL_error(L_, "out of memory while creating keeper states");
249 }
250
251 keepers->keeper_array[_i].L = _K;
252
253 if (keepers->gc_threshold >= 0) {
254 lua_gc(_K, LUA_GCSTOP, 0);
255 }
256
257 STACK_CHECK_START_ABS(_K, 0);
258
259 // copy the universe pointer in the keeper itself
260 universe_store(_K, this);
261 STACK_CHECK(_K, 0);
262
263 // make sure 'package' is initialized in keeper states, so that we have require()
264 // this because this is needed when transferring deep userdata object
265 luaL_requiref(_K, LUA_LOADLIBNAME, luaopen_package, 1); // L_: settings K: package
266 lua_pop(_K, 1); // L_: settings K:
267 STACK_CHECK(_K, 0);
268 serialize_require(DEBUGSPEW_PARAM_COMMA(this) _K);
269 STACK_CHECK(_K, 0);
270
271 // copy package.path and package.cpath from the source state
272 if (luaG_getmodule(L_, LUA_LOADLIBNAME) != LuaType::NIL) { // L_: settings package K:
273 // when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately
274 InterCopyContext _c{ this, DestState{ _K }, SourceState{ L_ }, {}, SourceIndex{ lua_absindex(L_, -1) }, {}, LookupMode::ToKeeper, {} };
275 if (_c.inter_copy_package() != InterCopyResult::Success) { // L_: settings ... error_msg K:
276 // if something went wrong, the error message is at the top of the stack
277 lua_remove(L_, -2); // L_: settings error_msg
278 raise_lua_error(L_);
279 }
280 }
281 lua_pop(L_, 1); // L_: settings K:
282 STACK_CHECK(L_, 0);
283 STACK_CHECK(_K, 0);
284
285 // attempt to call on_state_create(), if we have one and it is a C function
286 // (only support a C function because we can't transfer executable Lua code in keepers)
287 // will raise an error in L_ in case of problem
288 CallOnStateCreate(this, _K, L_, LookupMode::ToKeeper);
289
290 // to see VM name in Decoda debugger
291 lua_pushfstring(_K, "Keeper #%d", _i + 1); // L_: settings K: "Keeper #n"
292 lua_setglobal(_K, "decoda_name"); // L_: settings K:
293 // create the fifos table in the keeper state
294 Keepers::CreateFifosTable(_K);
295 STACK_CHECK(_K, 0);
296 }
297 STACK_CHECK(L_, 0);
298}
299
300// #################################################################################################
81 301
82void Universe::terminateFreeRunningLanes(lua_State* L_, lua_Duration shutdownTimeout_, CancelOp op_) 302void Universe::terminateFreeRunningLanes(lua_State* L_, lua_Duration shutdownTimeout_, CancelOp op_)
83{ 303{
@@ -160,7 +380,7 @@ int universe_gc(lua_State* L_)
160 _U->timerLinda = nullptr; 380 _U->timerLinda = nullptr;
161 } 381 }
162 382
163 close_keepers(_U); 383 _U->closeKeepers();
164 384
165 // remove the protected allocator, if any 385 // remove the protected allocator, if any
166 _U->protectedAllocator.removeFrom(L_); 386 _U->protectedAllocator.removeFrom(L_);
diff --git a/src/universe.h b/src/universe.h
index 8244980..4ed1776 100644
--- a/src/universe.h
+++ b/src/universe.h
@@ -9,7 +9,7 @@ extern "C"
9} 9}
10#endif // __cplusplus 10#endif // __cplusplus
11 11
12#include "macros_and_utils.h" 12#include "lanesconf.h"
13#include "tracker.h" 13#include "tracker.h"
14#include "uniquekey.h" 14#include "uniquekey.h"
15 15
@@ -54,11 +54,6 @@ class AllocatorDefinition
54 allocF = lua_getallocf(L_, &allocUD); 54 allocF = lua_getallocf(L_, &allocUD);
55 } 55 }
56 56
57 void* lua_alloc(void* ptr_, size_t osize_, size_t nsize_)
58 {
59 allocF(allocUD, ptr_, osize_, nsize_);
60 }
61
62 void* alloc(size_t nsize_) 57 void* alloc(size_t nsize_)
63 { 58 {
64 return allocF(allocUD, nullptr, 0, nsize_); 59 return allocF(allocUD, nullptr, 0, nsize_);
@@ -185,6 +180,9 @@ class Universe
185 Universe& operator=(Universe const&) = delete; 180 Universe& operator=(Universe const&) = delete;
186 Universe& operator=(Universe&&) = delete; 181 Universe& operator=(Universe&&) = delete;
187 182
183 void closeKeepers();
184 void initializeAllocatorFunction(lua_State* L_);
185 void initializeKeepers(lua_State* L_);
188 void terminateFreeRunningLanes(lua_State* L_, lua_Duration shutdownTimeout_, CancelOp op_); 186 void terminateFreeRunningLanes(lua_State* L_, lua_Duration shutdownTimeout_, CancelOp op_);
189}; 187};
190 188
@@ -196,32 +194,6 @@ void universe_store(lua_State* L_, Universe* U_);
196 194
197// ################################################################################################# 195// #################################################################################################
198 196
199#if USE_DEBUG_SPEW()
200class DebugSpewIndentScope
201{
202 private:
203 Universe* const U;
204
205 public:
206 static char const* const debugspew_indent;
207
208 DebugSpewIndentScope(Universe* U_)
209 : U{ U_ }
210 {
211 if (U)
212 U->debugspewIndentDepth.fetch_add(1, std::memory_order_relaxed);
213 }
214
215 ~DebugSpewIndentScope()
216 {
217 if (U)
218 U->debugspewIndentDepth.fetch_sub(1, std::memory_order_relaxed);
219 }
220};
221#endif // USE_DEBUG_SPEW()
222
223// #################################################################################################
224
225[[nodiscard]] inline Universe* universe_get(lua_State* L_) 197[[nodiscard]] inline Universe* universe_get(lua_State* L_)
226{ 198{
227 STACK_CHECK_START_REL(L_, 0); 199 STACK_CHECK_START_REL(L_, 0);