diff options
-rw-r--r-- | deep_test/deep_test.cpp | 25 | ||||
-rw-r--r-- | lanes-4.0.0-0.rockspec | 1 | ||||
-rw-r--r-- | src/Makefile | 2 | ||||
-rw-r--r-- | src/deep.h | 16 | ||||
-rw-r--r-- | src/lanesconf.h | 6 | ||||
-rw-r--r-- | src/linda.cpp | 164 | ||||
-rw-r--r-- | src/linda.h | 5 | ||||
-rw-r--r-- | src/lindafactory.cpp | 136 | ||||
-rw-r--r-- | src/lindafactory.h | 28 |
9 files changed, 220 insertions, 163 deletions
diff --git a/deep_test/deep_test.cpp b/deep_test/deep_test.cpp index 3a58d81..1931e6d 100644 --- a/deep_test/deep_test.cpp +++ b/deep_test/deep_test.cpp | |||
@@ -7,18 +7,21 @@ | |||
7 | 7 | ||
8 | class MyDeepFactory : public DeepFactory | 8 | class MyDeepFactory : public DeepFactory |
9 | { | 9 | { |
10 | public: | ||
11 | |||
12 | static MyDeepFactory Instance; | ||
13 | |||
10 | private: | 14 | private: |
11 | 15 | ||
12 | DeepPrelude* newDeepObjectInternal(lua_State* L) const override; | 16 | DeepPrelude* newDeepObjectInternal(lua_State* L) const override; |
13 | void deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) const override; | 17 | void deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) const override; |
14 | void createMetatable(lua_State* L) const override | 18 | void createMetatable(lua_State* L_) const override |
15 | { | 19 | { |
16 | luaL_getmetatable(L, "deep"); | 20 | luaL_getmetatable(L_, "deep"); |
17 | } | 21 | } |
18 | char const* moduleName() const override { return "deep_test"; } | 22 | char const* moduleName() const override { return "deep_test"; } |
19 | }; | 23 | }; |
20 | 24 | /*static*/ MyDeepFactory MyDeepFactory::Instance{}; | |
21 | static MyDeepFactory g_MyDeepFactory; | ||
22 | 25 | ||
23 | // ################################################################################################# | 26 | // ################################################################################################# |
24 | 27 | ||
@@ -32,7 +35,7 @@ struct MyDeepUserdata : public DeepPrelude // Deep userdata MUST start with a De | |||
32 | 35 | ||
33 | DeepPrelude* MyDeepFactory::newDeepObjectInternal(lua_State* L) const | 36 | DeepPrelude* MyDeepFactory::newDeepObjectInternal(lua_State* L) const |
34 | { | 37 | { |
35 | MyDeepUserdata* deep_test = new MyDeepUserdata{ g_MyDeepFactory }; | 38 | MyDeepUserdata* deep_test = new MyDeepUserdata{ MyDeepFactory::Instance }; |
36 | return deep_test; | 39 | return deep_test; |
37 | } | 40 | } |
38 | 41 | ||
@@ -48,7 +51,7 @@ void MyDeepFactory::deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) cons | |||
48 | 51 | ||
49 | [[nodiscard]] static int deep_set(lua_State* L) | 52 | [[nodiscard]] static int deep_set(lua_State* L) |
50 | { | 53 | { |
51 | MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(g_MyDeepFactory.toDeep(L, 1)) }; | 54 | MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(MyDeepFactory::Instance.toDeep(L, 1)) }; |
52 | lua_Integer i = lua_tointeger( L, 2); | 55 | lua_Integer i = lua_tointeger( L, 2); |
53 | self->val = i; | 56 | self->val = i; |
54 | return 0; | 57 | return 0; |
@@ -58,7 +61,7 @@ void MyDeepFactory::deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) cons | |||
58 | 61 | ||
59 | [[nodiscard]] static int deep_setuv(lua_State* L) | 62 | [[nodiscard]] static int deep_setuv(lua_State* L) |
60 | { | 63 | { |
61 | MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(g_MyDeepFactory.toDeep(L, 1)) }; | 64 | MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(MyDeepFactory::Instance.toDeep(L, 1)) }; |
62 | int uv = (int) luaL_optinteger(L, 2, 1); | 65 | int uv = (int) luaL_optinteger(L, 2, 1); |
63 | lua_settop( L, 3); | 66 | lua_settop( L, 3); |
64 | lua_pushboolean( L, lua_setiuservalue( L, 1, uv) != 0); | 67 | lua_pushboolean( L, lua_setiuservalue( L, 1, uv) != 0); |
@@ -70,7 +73,7 @@ void MyDeepFactory::deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) cons | |||
70 | // won't actually do anything as deep userdata don't have uservalue slots | 73 | // won't actually do anything as deep userdata don't have uservalue slots |
71 | [[nodiscard]] static int deep_getuv(lua_State* L) | 74 | [[nodiscard]] static int deep_getuv(lua_State* L) |
72 | { | 75 | { |
73 | MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(g_MyDeepFactory.toDeep(L, 1)) }; | 76 | MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(MyDeepFactory::Instance.toDeep(L, 1)) }; |
74 | int uv = (int) luaL_optinteger(L, 2, 1); | 77 | int uv = (int) luaL_optinteger(L, 2, 1); |
75 | lua_getiuservalue( L, 1, uv); | 78 | lua_getiuservalue( L, 1, uv); |
76 | return 1; | 79 | return 1; |
@@ -80,7 +83,7 @@ void MyDeepFactory::deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) cons | |||
80 | 83 | ||
81 | [[nodiscard]] static int deep_tostring(lua_State* L) | 84 | [[nodiscard]] static int deep_tostring(lua_State* L) |
82 | { | 85 | { |
83 | MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(g_MyDeepFactory.toDeep(L, 1)) }; | 86 | MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(MyDeepFactory::Instance.toDeep(L, 1)) }; |
84 | lua_pushfstring(L, "%p:deep(%d)", lua_topointer(L, 1), self->val); | 87 | lua_pushfstring(L, "%p:deep(%d)", lua_topointer(L, 1), self->val); |
85 | return 1; | 88 | return 1; |
86 | } | 89 | } |
@@ -89,7 +92,7 @@ void MyDeepFactory::deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) cons | |||
89 | 92 | ||
90 | [[nodiscard]] static int deep_gc(lua_State* L) | 93 | [[nodiscard]] static int deep_gc(lua_State* L) |
91 | { | 94 | { |
92 | MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(g_MyDeepFactory.toDeep(L, 1)) }; | 95 | MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(MyDeepFactory::Instance.toDeep(L, 1)) }; |
93 | return 0; | 96 | return 0; |
94 | } | 97 | } |
95 | 98 | ||
@@ -111,7 +114,7 @@ int luaD_new_deep( lua_State* L) | |||
111 | { | 114 | { |
112 | int const nuv{ static_cast<int>(luaL_optinteger(L, 1, 0)) }; | 115 | int const nuv{ static_cast<int>(luaL_optinteger(L, 1, 0)) }; |
113 | lua_settop(L, 0); | 116 | lua_settop(L, 0); |
114 | return g_MyDeepFactory.pushDeepUserdata(Dest{ L }, nuv); | 117 | return MyDeepFactory::Instance.pushDeepUserdata(DestState{ L }, nuv); |
115 | } | 118 | } |
116 | 119 | ||
117 | // ################################################################################################# | 120 | // ################################################################################################# |
diff --git a/lanes-4.0.0-0.rockspec b/lanes-4.0.0-0.rockspec index 4e1b370..76982f1 100644 --- a/lanes-4.0.0-0.rockspec +++ b/lanes-4.0.0-0.rockspec | |||
@@ -66,6 +66,7 @@ build = { | |||
66 | "src/keeper.cpp", | 66 | "src/keeper.cpp", |
67 | "src/lanes.cpp", | 67 | "src/lanes.cpp", |
68 | "src/linda.cpp", | 68 | "src/linda.cpp", |
69 | "src/lindafactory.cpp", | ||
69 | "src/tools.cpp", | 70 | "src/tools.cpp", |
70 | "src/state.cpp", | 71 | "src/state.cpp", |
71 | "src/threading.cpp", | 72 | "src/threading.cpp", |
diff --git a/src/Makefile b/src/Makefile index d6bdfd7..06bbcd0 100644 --- a/src/Makefile +++ b/src/Makefile | |||
@@ -7,7 +7,7 @@ | |||
7 | 7 | ||
8 | MODULE=lanes | 8 | MODULE=lanes |
9 | 9 | ||
10 | SRC=lanes.c cancel.cpp compat.cpp threading.cpp tools.cpp state.cpp linda.cpp deep.cpp keeper.cpp universe.cpp | 10 | SRC=lanes.c cancel.cpp compat.cpp threading.cpp tools.cpp state.cpp linda.cpp lindafactory.cpp deep.cpp keeper.cpp universe.cpp |
11 | 11 | ||
12 | OBJ=$(SRC:.c=.o) | 12 | OBJ=$(SRC:.c=.o) |
13 | 13 | ||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | /* | 3 | /* |
4 | * public 'deep' API to be used by external modules if they want to implement Lanes-aware userdata | 4 | * public 'deep' API to be used by external modules if they want to implement Lanes-aware userdata |
5 | * said modules will have to link against lanes (it is not really possible to separate the 'deep userdata' implementation from the rest of Lanes) | 5 | * said modules can either link against lanes, or embed compat.cpp/h deep.cpp/h tools.cpp/h universe.cpp/h |
6 | */ | 6 | */ |
7 | 7 | ||
8 | #ifdef __cplusplus | 8 | #ifdef __cplusplus |
@@ -69,16 +69,16 @@ class DeepFactory | |||
69 | private: | 69 | private: |
70 | 70 | ||
71 | // NVI: private overrides | 71 | // NVI: private overrides |
72 | virtual DeepPrelude* newDeepObjectInternal(lua_State* L) const = 0; | 72 | virtual DeepPrelude* newDeepObjectInternal(lua_State* L_) const = 0; |
73 | virtual void deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) const = 0; | 73 | virtual void deleteDeepObjectInternal(lua_State* L_, DeepPrelude* o_) const = 0; |
74 | virtual void createMetatable(lua_State* L) const = 0; | 74 | virtual void createMetatable(lua_State* L_) const = 0; |
75 | virtual char const* moduleName() const = 0; | 75 | virtual char const* moduleName() const = 0; |
76 | 76 | ||
77 | public: | 77 | public: |
78 | 78 | ||
79 | // NVI: public interface | 79 | // NVI: public interface |
80 | int pushDeepUserdata(DestState L, int nuv_) const; | 80 | int pushDeepUserdata(DestState L_, int nuv_) const; |
81 | DeepPrelude* toDeep(lua_State* L, int index) const; | 81 | DeepPrelude* toDeep(lua_State* L_, int index_) const; |
82 | static void DeleteDeepObject(lua_State* L, DeepPrelude* o_); | 82 | static void DeleteDeepObject(lua_State* L_, DeepPrelude* o_); |
83 | static char const* PushDeepProxy(DestState L, DeepPrelude* prelude, int nuv_, LookupMode mode_); | 83 | static char const* PushDeepProxy(DestState L_, DeepPrelude* o_, int nuv_, LookupMode mode_); |
84 | }; | 84 | }; |
diff --git a/src/lanesconf.h b/src/lanesconf.h index 8c66bd1..7b4ff93 100644 --- a/src/lanesconf.h +++ b/src/lanesconf.h | |||
@@ -19,10 +19,8 @@ | |||
19 | // file-level static variable: in 'global' namespace, prefix s, followed by an uppercase letter | 19 | // file-level static variable: in 'global' namespace, prefix s, followed by an uppercase letter |
20 | // file-level function (static or not): no prefix, start with an uppercase letter | 20 | // file-level function (static or not): no prefix, start with an uppercase letter |
21 | // class/struct/enum type: no prefix, start with an uppercase letter | 21 | // class/struct/enum type: no prefix, start with an uppercase letter |
22 | // static class member: prefix s, followed by an uppercase letter | 22 | // static class member/method: no prefix, start with an uppercase letter |
23 | // regular class member: no prefix, start with a lowercase letter | 23 | // regular class member/method: no prefix, start with a lowercase letter |
24 | // static class method: no prefix, start with an uppercase letter | ||
25 | // regular class method: no prefix, start with a lowercase letter | ||
26 | // function argument: suffix _ | 24 | // function argument: suffix _ |
27 | // static function variable: prefix s, followed by an uppercase letter | 25 | // static function variable: prefix s, followed by an uppercase letter |
28 | // function local variable: prefix l, followed by an uppercase letter | 26 | // function local variable: prefix l, followed by an uppercase letter |
diff --git a/src/linda.cpp b/src/linda.cpp index 590b487..bc931ac 100644 --- a/src/linda.cpp +++ b/src/linda.cpp | |||
@@ -7,7 +7,7 @@ | |||
7 | /* | 7 | /* |
8 | =============================================================================== | 8 | =============================================================================== |
9 | 9 | ||
10 | Copyright (C) 2018 benoit Germain <bnt.germain@gmail.com> | 10 | Copyright (C) 2018-2024 benoit Germain <bnt.germain@gmail.com> |
11 | 11 | ||
12 | Permission is hereby granted, free of charge, to any person obtaining a copy | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy |
13 | of this software and associated documentation files (the "Software"), to deal | 13 | of this software and associated documentation files (the "Software"), to deal |
@@ -35,29 +35,13 @@ THE SOFTWARE. | |||
35 | #include "compat.h" | 35 | #include "compat.h" |
36 | #include "keeper.h" | 36 | #include "keeper.h" |
37 | #include "lanes_private.h" | 37 | #include "lanes_private.h" |
38 | #include "lindafactory.h" | ||
38 | #include "threading.h" | 39 | #include "threading.h" |
39 | #include "tools.h" | 40 | #include "tools.h" |
40 | #include "universe.h" | 41 | #include "universe.h" |
41 | 42 | ||
42 | #include <functional> | 43 | #include <functional> |
43 | 44 | ||
44 | // xxh64 of string "kLindaBatched" generated at https://www.pelock.com/products/hash-calculator | ||
45 | static constexpr UniqueKey kLindaBatched{ 0xB8234DF772646567ull, "linda.batched" }; | ||
46 | |||
47 | // ################################################################################################# | ||
48 | |||
49 | class LindaFactory : public DeepFactory | ||
50 | { | ||
51 | private: | ||
52 | |||
53 | DeepPrelude* newDeepObjectInternal(lua_State* L) const override; | ||
54 | void deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) const override; | ||
55 | void createMetatable(lua_State* L) const override; | ||
56 | char const* moduleName() const override; | ||
57 | }; | ||
58 | // I'm not totally happy with having a global variable. But since it's stateless, it will do for the time being. | ||
59 | static LindaFactory g_LindaFactory; | ||
60 | |||
61 | // ################################################################################################# | 45 | // ################################################################################################# |
62 | // ################################################################################################# | 46 | // ################################################################################################# |
63 | 47 | ||
@@ -67,7 +51,7 @@ static LindaFactory g_LindaFactory; | |||
67 | static constexpr uintptr_t kPointerMagicShift{ 3 }; | 51 | static constexpr uintptr_t kPointerMagicShift{ 3 }; |
68 | 52 | ||
69 | Linda::Linda(Universe* U_, LindaGroup group_, char const* name_, size_t len_) | 53 | Linda::Linda(Universe* U_, LindaGroup group_, char const* name_, size_t len_) |
70 | : DeepPrelude{ g_LindaFactory } | 54 | : DeepPrelude{ LindaFactory::Instance } |
71 | , U{ U_ } | 55 | , U{ U_ } |
72 | , m_keeper_index{ (group_ ? group_ : static_cast<int>(std::bit_cast<uintptr_t>(this) >> kPointerMagicShift)) % U_->keepers->nb_keepers } | 56 | , m_keeper_index{ (group_ ? group_ : static_cast<int>(std::bit_cast<uintptr_t>(this) >> kPointerMagicShift)) % U_->keepers->nb_keepers } |
73 | { | 57 | { |
@@ -132,7 +116,7 @@ char const* Linda::getName() const | |||
132 | template <bool OPT> | 116 | template <bool OPT> |
133 | [[nodiscard]] static inline Linda* ToLinda(lua_State* L_, int idx_) | 117 | [[nodiscard]] static inline Linda* ToLinda(lua_State* L_, int idx_) |
134 | { | 118 | { |
135 | Linda* const linda{ static_cast<Linda*>(g_LindaFactory.toDeep(L_, idx_)) }; | 119 | Linda* const linda{ static_cast<Linda*>(LindaFactory::Instance.toDeep(L_, idx_)) }; |
136 | if constexpr (!OPT) | 120 | if constexpr (!OPT) |
137 | { | 121 | { |
138 | luaL_argcheck(L_, linda != nullptr, idx_, "expecting a linda object"); // doesn't return if linda is nullptr | 122 | luaL_argcheck(L_, linda != nullptr, idx_, "expecting a linda object"); // doesn't return if linda is nullptr |
@@ -856,124 +840,26 @@ LUAG_FUNC(linda_towatch) | |||
856 | 840 | ||
857 | // ################################################################################################# | 841 | // ################################################################################################# |
858 | 842 | ||
859 | DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* L) const | 843 | namespace global { |
860 | { | 844 | static luaL_Reg const sLindaMT[] = { |
861 | size_t name_len = 0; | 845 | { "__concat", LG_linda_concat }, |
862 | char const* linda_name = nullptr; | 846 | { "__tostring", LG_linda_tostring }, |
863 | LindaGroup linda_group{ 0 }; | 847 | { "__towatch", LG_linda_towatch }, // Decoda __towatch support |
864 | // should have a string and/or a number of the stack as parameters (name and group) | 848 | { "cancel", LG_linda_cancel }, |
865 | switch (lua_gettop(L)) | 849 | { "count", LG_linda_count }, |
866 | { | 850 | { "deep", LG_linda_deep }, |
867 | default: // 0 | 851 | { "dump", LG_linda_dump }, |
868 | break; | 852 | { "get", LG_linda_get }, |
869 | 853 | { "limit", LG_linda_limit }, | |
870 | case 1: // 1 parameter, either a name or a group | 854 | { "receive", LG_linda_receive }, |
871 | if (lua_type(L, -1) == LUA_TSTRING) | 855 | { "send", LG_linda_send }, |
872 | { | 856 | { "set", LG_linda_set }, |
873 | linda_name = lua_tolstring(L, -1, &name_len); | 857 | { nullptr, nullptr } |
874 | } | 858 | }; |
875 | else | 859 | } // namespace global |
876 | { | 860 | // it's somewhat awkward to instanciate the LindaFactory here instead of lindafactory.cpp, |
877 | linda_group = LindaGroup{ static_cast<int>(lua_tointeger(L, -1)) }; | 861 | // but that's necessary to provide s_LindaMT without exposing it outside linda.cpp. |
878 | } | 862 | /*static*/ LindaFactory LindaFactory::Instance{ global::sLindaMT }; |
879 | break; | ||
880 | |||
881 | case 2: // 2 parameters, a name and group, in that order | ||
882 | linda_name = lua_tolstring(L, -2, &name_len); | ||
883 | linda_group = LindaGroup{ static_cast<int>(lua_tointeger(L, -1)) }; | ||
884 | break; | ||
885 | } | ||
886 | |||
887 | /* The deep data is allocated separately of Lua stack; we might no | ||
888 | * longer be around when last reference to it is being released. | ||
889 | * One can use any memory allocation scheme. | ||
890 | * just don't use L's allocF because we don't know which state will get the honor of GCing the linda | ||
891 | */ | ||
892 | Universe* const U{ universe_get(L) }; | ||
893 | Linda* linda{ new (U) Linda{ U, linda_group, linda_name, name_len } }; | ||
894 | return linda; | ||
895 | } | ||
896 | |||
897 | // ################################################################################################# | ||
898 | |||
899 | void LindaFactory::deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) const | ||
900 | { | ||
901 | Linda* const linda{ static_cast<Linda*>(o_) }; | ||
902 | LUA_ASSERT(L, linda); | ||
903 | Keeper* const myK{ linda->whichKeeper() }; | ||
904 | // if collected after the universe, keepers are already destroyed, and there is nothing to clear | ||
905 | if (myK) | ||
906 | { | ||
907 | // if collected from my own keeper, we can't acquire/release it | ||
908 | // because we are already inside a protected area, and trying to do so would deadlock! | ||
909 | bool const need_acquire_release{ myK->L != L }; | ||
910 | // Clean associated structures in the keeper state. | ||
911 | Keeper* const K{ need_acquire_release ? linda->acquireKeeper() : myK }; | ||
912 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... | ||
913 | [[maybe_unused]] KeeperCallResult const result{ keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0) }; | ||
914 | LUA_ASSERT(L, result.has_value() && result.value() == 0); | ||
915 | if (need_acquire_release) | ||
916 | { | ||
917 | linda->releaseKeeper(K); | ||
918 | } | ||
919 | } | ||
920 | |||
921 | delete linda; // operator delete overload ensures things go as expected | ||
922 | } | ||
923 | |||
924 | // ################################################################################################# | ||
925 | |||
926 | static luaL_Reg const s_LindaMT[] = | ||
927 | { | ||
928 | { "__concat", LG_linda_concat }, | ||
929 | { "__tostring", LG_linda_tostring }, | ||
930 | { "__towatch", LG_linda_towatch }, // Decoda __towatch support | ||
931 | { "cancel", LG_linda_cancel }, | ||
932 | { "count", LG_linda_count }, | ||
933 | { "deep", LG_linda_deep }, | ||
934 | { "dump", LG_linda_dump }, | ||
935 | { "get", LG_linda_get }, | ||
936 | { "limit", LG_linda_limit }, | ||
937 | { "receive", LG_linda_receive }, | ||
938 | { "send", LG_linda_send }, | ||
939 | { "set", LG_linda_set }, | ||
940 | { nullptr, nullptr } | ||
941 | }; | ||
942 | |||
943 | void LindaFactory::createMetatable(lua_State* L) const | ||
944 | { | ||
945 | STACK_CHECK_START_REL(L, 0); | ||
946 | lua_newtable(L); | ||
947 | // metatable is its own index | ||
948 | lua_pushvalue(L, -1); | ||
949 | lua_setfield(L, -2, "__index"); | ||
950 | |||
951 | // protect metatable from external access | ||
952 | lua_pushliteral(L, "Linda"); | ||
953 | lua_setfield(L, -2, "__metatable"); | ||
954 | |||
955 | // the linda functions | ||
956 | luaL_setfuncs(L, s_LindaMT, 0); | ||
957 | |||
958 | // some constants | ||
959 | kLindaBatched.pushKey(L); | ||
960 | lua_setfield(L, -2, "batched"); | ||
961 | |||
962 | kNilSentinel.pushKey(L); | ||
963 | lua_setfield(L, -2, "null"); | ||
964 | |||
965 | STACK_CHECK(L, 1); | ||
966 | } | ||
967 | |||
968 | // ################################################################################################# | ||
969 | |||
970 | char const* LindaFactory::moduleName() const | ||
971 | { | ||
972 | // linda is a special case because we know lanes must be loaded from the main lua state | ||
973 | // to be able to ever get here, so we know it will remain loaded as long a the main state is around | ||
974 | // in other words, forever. | ||
975 | return nullptr; | ||
976 | } | ||
977 | 863 | ||
978 | // ################################################################################################# | 864 | // ################################################################################################# |
979 | 865 | ||
@@ -996,5 +882,5 @@ LUAG_FUNC(linda) | |||
996 | luaL_checktype(L, 1, LUA_TSTRING); | 882 | luaL_checktype(L, 1, LUA_TSTRING); |
997 | luaL_checktype(L, 2, LUA_TNUMBER); | 883 | luaL_checktype(L, 2, LUA_TNUMBER); |
998 | } | 884 | } |
999 | return g_LindaFactory.pushDeepUserdata(DestState{ L }, 0); | 885 | return LindaFactory::Instance.pushDeepUserdata(DestState{ L }, 0); |
1000 | } | 886 | } |
diff --git a/src/linda.h b/src/linda.h index f3b1844..21b6ecc 100644 --- a/src/linda.h +++ b/src/linda.h | |||
@@ -13,6 +13,11 @@ struct Keeper; | |||
13 | 13 | ||
14 | // ################################################################################################# | 14 | // ################################################################################################# |
15 | 15 | ||
16 | // xxh64 of string "kLindaBatched" generated at https://www.pelock.com/products/hash-calculator | ||
17 | static constexpr UniqueKey kLindaBatched{ 0xB8234DF772646567ull, "linda.batched" }; | ||
18 | |||
19 | // ################################################################################################# | ||
20 | |||
16 | using LindaGroup = Unique<int>; | 21 | using LindaGroup = Unique<int>; |
17 | 22 | ||
18 | class Linda : public DeepPrelude // Deep userdata MUST start with this header | 23 | class Linda : public DeepPrelude // Deep userdata MUST start with this header |
diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp new file mode 100644 index 0000000..81e472d --- /dev/null +++ b/src/lindafactory.cpp | |||
@@ -0,0 +1,136 @@ | |||
1 | /* | ||
2 | * LINDAFACTORY.CPP Copyright (c) 2024-, Benoit Germain | ||
3 | * | ||
4 | * Linda deep userdata factory | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | =============================================================================== | ||
9 | |||
10 | Copyright (C) 2024- benoit Germain <bnt.germain@gmail.com> | ||
11 | |||
12 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
13 | of this software and associated documentation files (the "Software"), to deal | ||
14 | in the Software without restriction, including without limitation the rights | ||
15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
16 | copies of the Software, and to permit persons to whom the Software is | ||
17 | furnished to do so, subject to the following conditions: | ||
18 | |||
19 | The above copyright notice and this permission notice shall be included in | ||
20 | all copies or substantial portions of the Software. | ||
21 | |||
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
28 | THE SOFTWARE. | ||
29 | |||
30 | =============================================================================== | ||
31 | */ | ||
32 | |||
33 | #include "lindafactory.h" | ||
34 | |||
35 | #include "linda.h" | ||
36 | |||
37 | // ################################################################################################# | ||
38 | |||
39 | void LindaFactory::createMetatable(lua_State* L_) const | ||
40 | { | ||
41 | STACK_CHECK_START_REL(L_, 0); | ||
42 | lua_newtable(L_); | ||
43 | // metatable is its own index | ||
44 | lua_pushvalue(L_, -1); | ||
45 | lua_setfield(L_, -2, "__index"); | ||
46 | |||
47 | // protect metatable from external access | ||
48 | lua_pushliteral(L_, "Linda"); | ||
49 | lua_setfield(L_, -2, "__metatable"); | ||
50 | |||
51 | // the linda functions | ||
52 | luaL_setfuncs(L_, mLindaMT, 0); | ||
53 | |||
54 | // some constants | ||
55 | kLindaBatched.pushKey(L_); | ||
56 | lua_setfield(L_, -2, "batched"); | ||
57 | |||
58 | kNilSentinel.pushKey(L_); | ||
59 | lua_setfield(L_, -2, "null"); | ||
60 | |||
61 | STACK_CHECK(L_, 1); | ||
62 | } | ||
63 | |||
64 | // ################################################################################################# | ||
65 | |||
66 | void LindaFactory::deleteDeepObjectInternal(lua_State* L_, DeepPrelude* o_) const | ||
67 | { | ||
68 | Linda* const linda{ static_cast<Linda*>(o_) }; | ||
69 | LUA_ASSERT(L_, linda); | ||
70 | Keeper* const myK{ linda->whichKeeper() }; | ||
71 | // if collected after the universe, keepers are already destroyed, and there is nothing to clear | ||
72 | if (myK) | ||
73 | { | ||
74 | // if collected from my own keeper, we can't acquire/release it | ||
75 | // because we are already inside a protected area, and trying to do so would deadlock! | ||
76 | bool const need_acquire_release{ myK->L != L_ }; | ||
77 | // Clean associated structures in the keeper state. | ||
78 | Keeper* const K{ need_acquire_release ? linda->acquireKeeper() : myK }; | ||
79 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... | ||
80 | [[maybe_unused]] KeeperCallResult const result{ keeper_call(linda->U, K->L, KEEPER_API(clear), L_, linda, 0) }; | ||
81 | LUA_ASSERT(L_, result.has_value() && result.value() == 0); | ||
82 | if (need_acquire_release) | ||
83 | { | ||
84 | linda->releaseKeeper(K); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | delete linda; // operator delete overload ensures things go as expected | ||
89 | } | ||
90 | |||
91 | // ################################################################################################# | ||
92 | |||
93 | char const* LindaFactory::moduleName() const | ||
94 | { | ||
95 | // linda is a special case because we know lanes must be loaded from the main lua state | ||
96 | // to be able to ever get here, so we know it will remain loaded as long a the main state is around | ||
97 | // in other words, forever. | ||
98 | return nullptr; | ||
99 | } | ||
100 | |||
101 | // ################################################################################################# | ||
102 | |||
103 | DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* L_) const | ||
104 | { | ||
105 | size_t name_len{ 0 }; | ||
106 | char const* linda_name{ nullptr }; | ||
107 | LindaGroup linda_group{ 0 }; | ||
108 | // should have a string and/or a number of the stack as parameters (name and group) | ||
109 | switch (lua_gettop(L_)) | ||
110 | { | ||
111 | default: // 0 | ||
112 | break; | ||
113 | |||
114 | case 1: // 1 parameter, either a name or a group | ||
115 | if (lua_type(L_, -1) == LUA_TSTRING) | ||
116 | { | ||
117 | linda_name = lua_tolstring(L_, -1, &name_len); | ||
118 | } | ||
119 | else | ||
120 | { | ||
121 | linda_group = LindaGroup{ static_cast<int>(lua_tointeger(L_, -1)) }; | ||
122 | } | ||
123 | break; | ||
124 | |||
125 | case 2: // 2 parameters, a name and group, in that order | ||
126 | linda_name = lua_tolstring(L_, -2, &name_len); | ||
127 | linda_group = LindaGroup{ static_cast<int>(lua_tointeger(L_, -1)) }; | ||
128 | break; | ||
129 | } | ||
130 | |||
131 | // The deep data is allocated separately of Lua stack; we might no longer be around when last reference to it is being released. | ||
132 | // One can use any memory allocation scheme. Just don't use L's allocF because we don't know which state will get the honor of GCing the linda | ||
133 | Universe* const U{ universe_get(L_) }; | ||
134 | Linda* const linda{ new (U) Linda{ U, linda_group, linda_name, name_len } }; | ||
135 | return linda; | ||
136 | } | ||
diff --git a/src/lindafactory.h b/src/lindafactory.h new file mode 100644 index 0000000..d31af1f --- /dev/null +++ b/src/lindafactory.h | |||
@@ -0,0 +1,28 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include "deep.h" | ||
4 | |||
5 | // ################################################################################################# | ||
6 | |||
7 | class LindaFactory | ||
8 | : public DeepFactory | ||
9 | { | ||
10 | public: | ||
11 | |||
12 | // I'm not totally happy with having a 'global' variable. Maybe it should be dynamically created and stored somewhere in the universe? | ||
13 | static LindaFactory Instance; | ||
14 | |||
15 | LindaFactory(luaL_Reg const lindaMT_[]) | ||
16 | : mLindaMT{ lindaMT_ } | ||
17 | { | ||
18 | } | ||
19 | |||
20 | private: | ||
21 | |||
22 | luaL_Reg const* const mLindaMT{ nullptr }; | ||
23 | |||
24 | void createMetatable(lua_State* L_) const override; | ||
25 | void deleteDeepObjectInternal(lua_State* L_, DeepPrelude* o_) const override; | ||
26 | char const* moduleName() const override; | ||
27 | DeepPrelude* newDeepObjectInternal(lua_State* L_) const override; | ||
28 | }; | ||