aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--deep_test/deep_test.cpp66
-rw-r--r--deep_test/deep_test.vcxproj14
-rw-r--r--docs/index.html39
-rw-r--r--src/deep.cpp149
-rw-r--r--src/deep.h62
-rw-r--r--src/keeper.cpp2
-rw-r--r--src/keeper.h2
-rw-r--r--src/lanes.cpp6
-rw-r--r--src/linda.cpp292
9 files changed, 318 insertions, 314 deletions
diff --git a/deep_test/deep_test.cpp b/deep_test/deep_test.cpp
index 3467939..b11445b 100644
--- a/deep_test/deep_test.cpp
+++ b/deep_test/deep_test.cpp
@@ -5,6 +5,21 @@
5#include <memory.h> 5#include <memory.h>
6#include <assert.h> 6#include <assert.h>
7 7
8class MyDeepFactory : public DeepFactory
9{
10 private:
11
12 DeepPrelude* newDeepObjectInternal(lua_State* L) const override;
13 void deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) const override;
14 void createMetatable(lua_State* L) const override
15 {
16 luaL_getmetatable(L, "deep");
17 }
18 char const* moduleName() const override { return "deep_test"; }
19};
20
21static MyDeepFactory g_MyDeepFactory;
22
8// ################################################################################################ 23// ################################################################################################
9 24
10// a lanes-deep userdata. needs DeepPrelude and luaG_newdeepuserdata from Lanes code. 25// a lanes-deep userdata. needs DeepPrelude and luaG_newdeepuserdata from Lanes code.
@@ -15,44 +30,25 @@ struct MyDeepUserdata : public DeepPrelude // Deep userdata MUST start with a De
15 30
16// ################################################################################################ 31// ################################################################################################
17 32
18[[nodiscard]] static void* deep_test_id(lua_State* L, DeepOp op_) 33DeepPrelude* MyDeepFactory::newDeepObjectInternal(lua_State* L) const
19{ 34{
20 switch( op_) 35 MyDeepUserdata* deep_test = new MyDeepUserdata{ g_MyDeepFactory };
21 { 36 return deep_test;
22 case DeepOp::New: 37}
23 {
24 MyDeepUserdata* deep_test = new MyDeepUserdata;
25 return deep_test;
26 }
27
28 case DeepOp::Delete:
29 {
30 MyDeepUserdata* deep_test = static_cast<MyDeepUserdata*>(lua_touserdata( L, 1));
31 delete deep_test;
32 return nullptr;
33 }
34
35 case DeepOp::Metatable:
36 {
37 luaL_getmetatable( L, "deep"); // mt
38 return nullptr;
39 }
40 38
41 case DeepOp::Module: 39// ################################################################################################
42 return (void*)"deep_test";
43 40
44 default: 41void MyDeepFactory::deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) const
45 { 42{
46 return nullptr; 43 MyDeepUserdata* deep_test = static_cast<MyDeepUserdata*>(o_);
47 } 44 delete deep_test;
48 }
49} 45}
50 46
51// ################################################################################################ 47// ################################################################################################
52 48
53[[nodiscard]] static int deep_set(lua_State* L) 49[[nodiscard]] static int deep_set(lua_State* L)
54{ 50{
55 MyDeepUserdata* self = static_cast<MyDeepUserdata*>(luaG_todeep(L, deep_test_id, 1)); 51 MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(g_MyDeepFactory.toDeep(L, 1)) };
56 lua_Integer i = lua_tointeger( L, 2); 52 lua_Integer i = lua_tointeger( L, 2);
57 self->val = i; 53 self->val = i;
58 return 0; 54 return 0;
@@ -60,10 +56,9 @@ struct MyDeepUserdata : public DeepPrelude // Deep userdata MUST start with a De
60 56
61// ################################################################################################ 57// ################################################################################################
62 58
63// won't actually do anything as deep userdata don't have uservalue slots
64[[nodiscard]] static int deep_setuv(lua_State* L) 59[[nodiscard]] static int deep_setuv(lua_State* L)
65{ 60{
66 MyDeepUserdata* self = static_cast<MyDeepUserdata*>(luaG_todeep(L, deep_test_id, 1)); 61 MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(g_MyDeepFactory.toDeep(L, 1)) };
67 int uv = (int) luaL_optinteger(L, 2, 1); 62 int uv = (int) luaL_optinteger(L, 2, 1);
68 lua_settop( L, 3); 63 lua_settop( L, 3);
69 lua_pushboolean( L, lua_setiuservalue( L, 1, uv) != 0); 64 lua_pushboolean( L, lua_setiuservalue( L, 1, uv) != 0);
@@ -75,7 +70,7 @@ struct MyDeepUserdata : public DeepPrelude // Deep userdata MUST start with a De
75// won't actually do anything as deep userdata don't have uservalue slots 70// won't actually do anything as deep userdata don't have uservalue slots
76[[nodiscard]] static int deep_getuv(lua_State* L) 71[[nodiscard]] static int deep_getuv(lua_State* L)
77{ 72{
78 MyDeepUserdata* self = static_cast<MyDeepUserdata*>(luaG_todeep(L, deep_test_id, 1)); 73 MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(g_MyDeepFactory.toDeep(L, 1)) };
79 int uv = (int) luaL_optinteger(L, 2, 1); 74 int uv = (int) luaL_optinteger(L, 2, 1);
80 lua_getiuservalue( L, 1, uv); 75 lua_getiuservalue( L, 1, uv);
81 return 1; 76 return 1;
@@ -85,7 +80,7 @@ struct MyDeepUserdata : public DeepPrelude // Deep userdata MUST start with a De
85 80
86[[nodiscard]] static int deep_tostring(lua_State* L) 81[[nodiscard]] static int deep_tostring(lua_State* L)
87{ 82{
88 MyDeepUserdata* self = static_cast<MyDeepUserdata*>(luaG_todeep(L, deep_test_id, 1)); 83 MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(g_MyDeepFactory.toDeep(L, 1)) };
89 lua_pushfstring(L, "%p:deep(%d)", lua_topointer(L, 1), self->val); 84 lua_pushfstring(L, "%p:deep(%d)", lua_topointer(L, 1), self->val);
90 return 1; 85 return 1;
91} 86}
@@ -94,7 +89,7 @@ struct MyDeepUserdata : public DeepPrelude // Deep userdata MUST start with a De
94 89
95[[nodiscard]] static int deep_gc(lua_State* L) 90[[nodiscard]] static int deep_gc(lua_State* L)
96{ 91{
97 MyDeepUserdata* self = static_cast<MyDeepUserdata*>(luaG_todeep(L, deep_test_id, 1)); 92 MyDeepUserdata* const self{ static_cast<MyDeepUserdata*>(g_MyDeepFactory.toDeep(L, 1)) };
98 return 0; 93 return 0;
99} 94}
100 95
@@ -115,9 +110,8 @@ static luaL_Reg const deep_mt[] =
115int luaD_new_deep( lua_State* L) 110int luaD_new_deep( lua_State* L)
116{ 111{
117 int const nuv{ static_cast<int>(luaL_optinteger(L, 1, 0)) }; 112 int const nuv{ static_cast<int>(luaL_optinteger(L, 1, 0)) };
118 // no additional parameter to luaG_newdeepuserdata!
119 lua_settop(L, 0); 113 lua_settop(L, 0);
120 return luaG_newdeepuserdata(Dest{ L }, deep_test_id, nuv); 114 return g_MyDeepFactory.pushDeepUserdata(Dest{ L }, nuv);
121} 115}
122 116
123// ################################################################################################ 117// ################################################################################################
diff --git a/deep_test/deep_test.vcxproj b/deep_test/deep_test.vcxproj
index 5cd3c55..ddfad5d 100644
--- a/deep_test/deep_test.vcxproj
+++ b/deep_test/deep_test.vcxproj
@@ -388,6 +388,7 @@
388 <AdditionalIncludeDirectories>$(SolutionDir)..\Lua53\include;$(SolutionDir)Lanes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> 388 <AdditionalIncludeDirectories>$(SolutionDir)..\Lua53\include;$(SolutionDir)Lanes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
389 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 389 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
390 <LanguageStandard>stdcpp20</LanguageStandard> 390 <LanguageStandard>stdcpp20</LanguageStandard>
391 <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
391 </ClCompile> 392 </ClCompile>
392 <PostBuildEvent> 393 <PostBuildEvent>
393 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\Lua53\bin\$(Platform)\Debug\</Command> 394 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\Lua53\bin\$(Platform)\Debug\</Command>
@@ -407,6 +408,7 @@
407 <AdditionalIncludeDirectories>$(SolutionDir)..\Lua51\include;$(SolutionDir)Lanes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> 408 <AdditionalIncludeDirectories>$(SolutionDir)..\Lua51\include;$(SolutionDir)Lanes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
408 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 409 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
409 <LanguageStandard>stdcpp20</LanguageStandard> 410 <LanguageStandard>stdcpp20</LanguageStandard>
411 <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
410 </ClCompile> 412 </ClCompile>
411 <PostBuildEvent> 413 <PostBuildEvent>
412 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\Lua51\bin\$(Platform)\Debug\</Command> 414 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\Lua51\bin\$(Platform)\Debug\</Command>
@@ -426,6 +428,7 @@
426 <AdditionalIncludeDirectories>$(SolutionDir)..\Lua51\include;$(SolutionDir)Lanes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> 428 <AdditionalIncludeDirectories>$(SolutionDir)..\Lua51\include;$(SolutionDir)Lanes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
427 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 429 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
428 <LanguageStandard>stdcpp20</LanguageStandard> 430 <LanguageStandard>stdcpp20</LanguageStandard>
431 <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
429 </ClCompile> 432 </ClCompile>
430 <PostBuildEvent> 433 <PostBuildEvent>
431 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\Lua52\bin\$(Platform)\Debug\</Command> 434 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\Lua52\bin\$(Platform)\Debug\</Command>
@@ -446,6 +449,7 @@
446 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 449 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
447 <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> 450 <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
448 <LanguageStandard>stdcpp20</LanguageStandard> 451 <LanguageStandard>stdcpp20</LanguageStandard>
452 <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
449 </ClCompile> 453 </ClCompile>
450 <PostBuildEvent> 454 <PostBuildEvent>
451 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\LuaJIT-2.1.0-beta3\bin\$(Platform)\</Command> 455 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\LuaJIT-2.1.0-beta3\bin\$(Platform)\</Command>
@@ -466,6 +470,7 @@
466 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 470 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
467 <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> 471 <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
468 <LanguageStandard>stdcpp20</LanguageStandard> 472 <LanguageStandard>stdcpp20</LanguageStandard>
473 <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
469 </ClCompile> 474 </ClCompile>
470 <PostBuildEvent> 475 <PostBuildEvent>
471 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\LuaJIT-2.0.5\bin\$(Platform)\</Command> 476 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\LuaJIT-2.0.5\bin\$(Platform)\</Command>
@@ -485,6 +490,7 @@
485 <AdditionalIncludeDirectories>$(SolutionDir)..\Lua54\include;$(SolutionDir)Lanes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> 490 <AdditionalIncludeDirectories>$(SolutionDir)..\Lua54\include;$(SolutionDir)Lanes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
486 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 491 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
487 <LanguageStandard>stdcpp20</LanguageStandard> 492 <LanguageStandard>stdcpp20</LanguageStandard>
493 <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
488 </ClCompile> 494 </ClCompile>
489 <PostBuildEvent> 495 <PostBuildEvent>
490 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\framework\</Command> 496 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\framework\</Command>
@@ -504,6 +510,7 @@
504 <AdditionalIncludeDirectories>$(SolutionDir)..\MoonJIT\src;$(SolutionDir)Lanes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> 510 <AdditionalIncludeDirectories>$(SolutionDir)..\MoonJIT\src;$(SolutionDir)Lanes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
505 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 511 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
506 <LanguageStandard>stdcpp20</LanguageStandard> 512 <LanguageStandard>stdcpp20</LanguageStandard>
513 <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
507 </ClCompile> 514 </ClCompile>
508 <PostBuildEvent> 515 <PostBuildEvent>
509 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\MoonJIT\bin\$(Platform)\</Command> 516 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\MoonJIT\bin\$(Platform)\</Command>
@@ -524,6 +531,7 @@
524 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 531 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
525 <PreprocessorDefinitions>_WINDLL;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> 532 <PreprocessorDefinitions>_WINDLL;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
526 <LanguageStandard>stdcpp20</LanguageStandard> 533 <LanguageStandard>stdcpp20</LanguageStandard>
534 <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
527 </ClCompile> 535 </ClCompile>
528 <PostBuildEvent> 536 <PostBuildEvent>
529 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\Lua53\bin\$(Platform)\Debug\</Command> 537 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\Lua53\bin\$(Platform)\Debug\</Command>
@@ -544,6 +552,7 @@
544 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 552 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
545 <PreprocessorDefinitions>_WINDLL;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> 553 <PreprocessorDefinitions>_WINDLL;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
546 <LanguageStandard>stdcpp20</LanguageStandard> 554 <LanguageStandard>stdcpp20</LanguageStandard>
555 <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
547 </ClCompile> 556 </ClCompile>
548 <PostBuildEvent> 557 <PostBuildEvent>
549 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\Lua51\bin\$(Platform)\Debug\</Command> 558 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\Lua51\bin\$(Platform)\Debug\</Command>
@@ -564,6 +573,7 @@
564 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 573 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
565 <PreprocessorDefinitions>_WINDLL;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> 574 <PreprocessorDefinitions>_WINDLL;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
566 <LanguageStandard>stdcpp20</LanguageStandard> 575 <LanguageStandard>stdcpp20</LanguageStandard>
576 <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
567 </ClCompile> 577 </ClCompile>
568 <PostBuildEvent> 578 <PostBuildEvent>
569 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\Lua52\bin\$(Platform)\Debug\</Command> 579 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\Lua52\bin\$(Platform)\Debug\</Command>
@@ -584,6 +594,7 @@
584 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 594 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
585 <PreprocessorDefinitions>_WINDLL;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> 595 <PreprocessorDefinitions>_WINDLL;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
586 <LanguageStandard>stdcpp20</LanguageStandard> 596 <LanguageStandard>stdcpp20</LanguageStandard>
597 <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
587 </ClCompile> 598 </ClCompile>
588 <PostBuildEvent> 599 <PostBuildEvent>
589 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\LuaJIT-2.1.0-beta3\bin\$(Platform)\</Command> 600 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\LuaJIT-2.1.0-beta3\bin\$(Platform)\</Command>
@@ -604,6 +615,7 @@
604 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 615 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
605 <PreprocessorDefinitions>_WINDLL;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> 616 <PreprocessorDefinitions>_WINDLL;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
606 <LanguageStandard>stdcpp20</LanguageStandard> 617 <LanguageStandard>stdcpp20</LanguageStandard>
618 <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
607 </ClCompile> 619 </ClCompile>
608 <PostBuildEvent> 620 <PostBuildEvent>
609 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\LuaJIT-2.0.5\bin\$(Platform)\</Command> 621 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\LuaJIT-2.0.5\bin\$(Platform)\</Command>
@@ -624,6 +636,7 @@
624 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 636 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
625 <PreprocessorDefinitions>_WINDLL;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> 637 <PreprocessorDefinitions>_WINDLL;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
626 <LanguageStandard>stdcpp20</LanguageStandard> 638 <LanguageStandard>stdcpp20</LanguageStandard>
639 <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
627 </ClCompile> 640 </ClCompile>
628 <PostBuildEvent> 641 <PostBuildEvent>
629 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\framework\</Command> 642 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\framework\</Command>
@@ -644,6 +657,7 @@
644 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 657 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
645 <PreprocessorDefinitions>_WINDLL;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> 658 <PreprocessorDefinitions>_WINDLL;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
646 <LanguageStandard>stdcpp20</LanguageStandard> 659 <LanguageStandard>stdcpp20</LanguageStandard>
660 <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
647 </ClCompile> 661 </ClCompile>
648 <PostBuildEvent> 662 <PostBuildEvent>
649 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\MoonJIT\bin\$(Platform)\</Command> 663 <Command>xcopy /R /F /Y /I "$(TargetPath)" $(SolutionDir)..\MoonJIT\bin\$(Platform)\</Command>
diff --git a/docs/index.html b/docs/index.html
index 3e535a6..67eccd5 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -1683,34 +1683,45 @@ int luaD_new_clonable(lua_State* L)
1683 1683
1684<ol> 1684<ol>
1685 <li> 1685 <li>
1686 Provide an <i>identity function</i> for your userdata, in C. This function is used for creation and deletion of your deep userdata (the shared resource), and for making metatables for the state-specific proxies for accessing it. The prototype is 1686 Provide a <i>factory</i> for your userdata. This object is used for creation and deletion of your deep userdata (the shared resource), and for making metatables for the state-specific proxies for accessing it. The prototype is
1687 <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> void* idfunc(lua_State* L, DeepOp op_);</pre></td></tr></table> 1687<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre>
1688 <tt>op_</tt> can be one of: 1688class MyDeepFactory : public DeepFactory
1689{
1690 private:
1691
1692 DeepPrelude* newDeepObjectInternal(lua_State* L) const override;
1693 void deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) const override;
1694 void createMetatable(lua_State* L) const override;
1695 char const* moduleName() const override;
1696};
1697
1698static MyDeepFactory g_MyDeepFactory;
1699</pre></td></tr></table>
1689 <ul> 1700 <ul>
1690 <li><tt>DeepOp::New</tt>: requests the creation of a new object, whose pointer is returned. Said object must derive from <tt>DeepPrelude</tt>.</li> 1701 <li><tt>newDeepObjectInternal</tt>: requests the creation of a new object, whose pointer is returned. Said object must derive from <tt>DeepPrelude</tt>.</li>
1691 <li><tt>DeepOp::Delete</tt>: receives this same pointer on the stack as a light userdata, and should cleanup the object.</li> 1702 <li><tt>deleteDeepObjectInternal</tt>: should cleanup the object.</li>
1692 <li><tt>DeepOp::Metatable</tt>: should build a metatable for the object. Don't cache the metatable yourself, Lanes takes care of it (<tt>DeepOp::Metatable</tt> should only be invoked once per state). Just push the metatable on the stack.</li> 1703 <li><tt>createMetatable</tt>: should build a metatable for the object. Don't cache the metatable yourself, Lanes takes care of it (<tt>createMetatable</tt> should only be invoked once per state). Just push the metatable on the stack.</li>
1693 <li><tt>DeepOp::Module</tt>: requests the name of the module that exports the idfunc, to be returned. It is necessary so that Lanes can require it in any lane state that receives a userdata. This is to prevent crashes in situations where the module could be unloaded while the idfunc pointer is still held.</li> 1704 <li><tt>moduleName</tt>: requests the name of the module that exports the factory, to be returned. It is necessary so that Lanes can require it in any lane state that receives a userdata. This is to prevent crashes in situations where the module could be unloaded while the factory pointer is still held.</li>
1694 </ul> 1705 </ul>
1695 Take a look at <tt>linda_id</tt> in <tt>lanes.cpp</tt> or <tt>deep_test_id</tt> in <tt>deep_test.cpp</tt>. 1706 Take a look at <tt>LindaFactory</tt> in <tt>linda.cpp</tt> or <tt>MyDeepFactory</tt> in <tt>deep_test.cpp</tt>.
1696 </li> 1707 </li>
1697 <li>Include <tt>"deep.h"</tt> and either link against Lanes or statically compile <tt>compat.cpp deep.cpp tools.cpp universe.cpp</tt> into your module if you want to avoid a runtime dependency for users that will use your module without Lanes. 1708 <li>Include <tt>"deep.h"</tt> and either link against Lanes or statically compile <tt>compat.cpp deep.cpp tools.cpp universe.cpp</tt> into your module if you want to avoid a runtime dependency for users that will use your module without Lanes.
1698 <li>Instanciate your userdata using <tt>luaG_newdeepuserdata()</tt>, instead of the regular <tt>lua_newuserdata()</tt>. Given an <tt>idfunc</tt>, it sets up the support structures and returns a state-specific proxy userdata for accessing your data. This proxy can also be copied over to other lanes.</li> 1709 <li>Instanciate your userdata using <tt>yourFactoryObject.pushDeepUserdata()()</tt>, instead of the regular <tt>lua_newuserdata()</tt>. Given a <tt>factory</tt>, it sets up the support structures and returns a state-specific proxy userdata for accessing your data. This proxy can also be copied over to other lanes.</li>
1699 <li>Accessing the deep userdata from your C code, use <tt>luaG_todeep()</tt> instead of the regular <tt>lua_touserdata()</tt>.</li> 1710 <li>Accessing the deep userdata from your C code, use <tt>yourFactoryObject.toDeep()</tt> instead of the regular <tt>lua_touserdata()</tt>.</li>
1700</ol> 1711</ol>
1701 1712
1702<p> 1713<p>
1703 Deep userdata management will take care of tying to <tt>__gc</tt> methods, and doing reference counting to see how many proxies are still there for accessing the data. Once there are none, the data will be freed through a call to the <tt>idfunc</tt> you provided. 1714 Deep userdata management will take care of tying to <tt>__gc</tt> methods, and doing reference counting to see how many proxies are still there for accessing the data. Once there are none, the data will be freed through a call to the factory you provided.
1704</p> 1715</p>
1705 1716
1706<p> 1717<p>
1707 Deep userdata in transit inside keeper states (sent in a linda but not yet consumed) don't call <tt>idfunc(DeepOp::Delete)</tt> and aren't considered by reference counting. The rationale is the following: 1718 Deep userdata in transit inside keeper states (sent in a linda but not yet consumed) don't call <tt>deleteDeepObjectInternal</tt> and aren't considered by reference counting. The rationale is the following:
1708 <br /> 1719 <br />
1709 If some non-keeper state holds a deep userdata for some deep object, then even if the keeper collects its own deep userdata, it shouldn't be cleaned up since the refcount is not 0. 1720 If some non-keeper state holds a deep userdata for some deep object, then even if the keeper collects its own deep userdata, it shouldn't be cleaned up since the refcount is not 0.
1710 <br /> 1721 <br />
1711 OTOH, if a keeper state holds the last deep userdata for some deep object, then no lane can do actual work with it. Deep userdata's <tt>idfunc()</tt> is never called from a keeper state. 1722 OTOH, if a keeper state holds the last deep userdata for some deep object, then no lane can do actual work with it. Deep userdata's <tt>factory()</tt> interface is never accessed from a keeper state.
1712 <br /> 1723 <br />
1713 Therefore, Lanes can just call <tt>idfunc(DeepOp::Delete)</tt> when the last non-keeper-held deep userdata is collected, as long as it doesn't do the same in a keeper state after that, since any remaining deep userdata in keeper states now hold stale pointers. 1724 Therefore, Lanes can just call <tt>deleteDeepObjectInternal</tt> when the last non-keeper-held deep userdata is collected, as long as it doesn't do the same in a keeper state after that, since any remaining deep userdata in keeper states now hold stale pointers.
1714</p> 1725</p>
1715 1726
1716<p> 1727<p>
diff --git a/src/deep.cpp b/src/deep.cpp
index d0b8123..780c86e 100644
--- a/src/deep.cpp
+++ b/src/deep.cpp
@@ -47,11 +47,11 @@ THE SOFTWARE.
47/*---=== Deep userdata ===---*/ 47/*---=== Deep userdata ===---*/
48 48
49/* 49/*
50* 'registry[REGKEY]' is a two-way lookup table for 'idfunc's and those type's 50* 'registry[REGKEY]' is a two-way lookup table for 'factory's and those type's
51* metatables: 51* metatables:
52* 52*
53* metatable -> idfunc 53* metatable -> factory
54* idfunc -> metatable 54* factory -> metatable
55*/ 55*/
56// crc64/we of string "DEEP_LOOKUP_KEY" generated at http://www.nitrxgen.net/hashgen/ 56// crc64/we of string "DEEP_LOOKUP_KEY" generated at http://www.nitrxgen.net/hashgen/
57static constexpr UniqueKey DEEP_LOOKUP_KEY{ 0x9fb9b4f3f633d83dull }; 57static constexpr UniqueKey DEEP_LOOKUP_KEY{ 0x9fb9b4f3f633d83dull };
@@ -84,8 +84,8 @@ static void set_deep_lookup(lua_State* L)
84// ################################################################################################ 84// ################################################################################################
85 85
86/* 86/*
87* Pops the key (metatable or idfunc) off the stack, and replaces with the 87* Pops the key (metatable or factory) off the stack, and replaces with the
88* deep lookup value (idfunc/metatable/nil). 88* deep lookup value (factory/metatable/nil).
89*/ 89*/
90static void get_deep_lookup(lua_State* L) 90static void get_deep_lookup(lua_State* L)
91{ 91{
@@ -104,21 +104,21 @@ static void get_deep_lookup(lua_State* L)
104// ################################################################################################ 104// ################################################################################################
105 105
106/* 106/*
107* Return the registered ID function for 'index' (deep userdata proxy), 107* Return the registered factory for 'index' (deep userdata proxy),
108* or nullptr if 'index' is not a deep userdata proxy. 108* or nullptr if 'index' is not a deep userdata proxy.
109*/ 109*/
110[[nodiscard]] static inline luaG_IdFunction get_idfunc(lua_State* L, int index, LookupMode mode_) 110[[nodiscard]] static inline DeepFactory* get_factory(lua_State* L, int index, LookupMode mode_)
111{ 111{
112 // when looking inside a keeper, we are 100% sure the object is a deep userdata 112 // when looking inside a keeper, we are 100% sure the object is a deep userdata
113 if (mode_ == LookupMode::FromKeeper) 113 if (mode_ == LookupMode::FromKeeper)
114 { 114 {
115 DeepPrelude** const proxy{ lua_tofulluserdata<DeepPrelude*>(L, index) }; 115 DeepPrelude* const proxy{ *lua_tofulluserdata<DeepPrelude*>(L, index) };
116 // we can (and must) cast and fetch the internally stored idfunc 116 // we can (and must) cast and fetch the internally stored factory
117 return (*proxy)->idfunc; 117 return &proxy->m_factory;
118 } 118 }
119 else 119 else
120 { 120 {
121 // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/idfunc database 121 // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/factory database
122 // it is the only way to ensure that the userdata is indeed a deep userdata! 122 // it is the only way to ensure that the userdata is indeed a deep userdata!
123 // of course, we could just trust the caller, but we won't 123 // of course, we could just trust the caller, but we won't
124 STACK_GROW( L, 1); 124 STACK_GROW( L, 1);
@@ -129,10 +129,10 @@ static void get_deep_lookup(lua_State* L)
129 return nullptr; // no metatable: can't be a deep userdata object! 129 return nullptr; // no metatable: can't be a deep userdata object!
130 } 130 }
131 131
132 // replace metatable with the idfunc pointer, if it is actually a deep userdata 132 // replace metatable with the factory pointer, if it is actually a deep userdata
133 get_deep_lookup( L); // deep ... idfunc|nil 133 get_deep_lookup( L); // deep ... factory|nil
134 134
135 luaG_IdFunction const ret{ *lua_tolightuserdata<luaG_IdFunction>(L, -1) }; // nullptr if not a userdata 135 DeepFactory* const ret{ lua_tolightuserdata<DeepFactory>(L, -1) }; // nullptr if not a userdata
136 lua_pop( L, 1); 136 lua_pop( L, 1);
137 STACK_CHECK( L, 0); 137 STACK_CHECK( L, 0);
138 return ret; 138 return ret;
@@ -141,14 +141,10 @@ static void get_deep_lookup(lua_State* L)
141 141
142// ################################################################################################ 142// ################################################################################################
143 143
144void free_deep_prelude(lua_State* L, DeepPrelude* prelude_) 144void DeepFactory::DeleteDeepObject(lua_State* L, DeepPrelude* o_)
145{ 145{
146 ASSERT_L(prelude_->idfunc);
147 STACK_CHECK_START_REL(L, 0); 146 STACK_CHECK_START_REL(L, 0);
148 // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup 147 o_->m_factory.deleteDeepObjectInternal(L, o_);
149 lua_pushlightuserdata( L, prelude_);
150 prelude_->idfunc( L, DeepOp::Delete);
151 lua_pop(L, 1);
152 STACK_CHECK(L, 0); 148 STACK_CHECK(L, 0);
153} 149}
154 150
@@ -162,8 +158,8 @@ void free_deep_prelude(lua_State* L, DeepPrelude* prelude_)
162 */ 158 */
163[[nodiscard]] static int deep_userdata_gc(lua_State* L) 159[[nodiscard]] static int deep_userdata_gc(lua_State* L)
164{ 160{
165 DeepPrelude** const proxy{ lua_tofulluserdata<DeepPrelude*>(L, 1) }; 161 DeepPrelude* const* const proxy{ lua_tofulluserdata<DeepPrelude*>(L, 1) };
166 DeepPrelude* p = *proxy; 162 DeepPrelude* const p{ *proxy };
167 163
168 // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded 164 // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded
169 // in that case, we are not multithreaded and locking isn't necessary anyway 165 // in that case, we are not multithreaded and locking isn't necessary anyway
@@ -178,17 +174,9 @@ void free_deep_prelude(lua_State* L, DeepPrelude* prelude_)
178 lua_insert( L, -2); // __gc self 174 lua_insert( L, -2); // __gc self
179 lua_call( L, 1, 0); // 175 lua_call( L, 1, 0); //
180 } 176 }
181 // 'idfunc' expects a clean stack to work on 177 // we don't really know what remains on the stack at that point (depending on us finding a __gc or not), but we don't care
182 lua_settop( L, 0); 178 DeepFactory::DeleteDeepObject(L, p);
183 free_deep_prelude( L, p);
184
185 // top was set to 0, then userdata was pushed. "delete" might want to pop the userdata (we don't care), but should not push anything!
186 if ( lua_gettop( L) > 1)
187 {
188 return luaL_error( L, "Bad idfunc(DeepOp::Delete): should not push anything");
189 }
190 } 179 }
191 *proxy = nullptr; // make sure we don't use it any more, just in case
192 return 0; 180 return 0;
193} 181}
194 182
@@ -196,14 +184,14 @@ void free_deep_prelude(lua_State* L, DeepPrelude* prelude_)
196 184
197/* 185/*
198 * Push a proxy userdata on the stack. 186 * Push a proxy userdata on the stack.
199 * returns nullptr if ok, else some error string related to bad idfunc behavior or module require problem 187 * returns nullptr if ok, else some error string related to bad factory behavior or module require problem
200 * (error cannot happen with mode_ == LookupMode::ToKeeper) 188 * (error cannot happen with mode_ == LookupMode::ToKeeper)
201 * 189 *
202 * Initializes necessary structures if it's the first time 'idfunc' is being 190 * Initializes necessary structures if it's the first time 'factory' is being
203 * used in this Lua state (metatable, registring it). Otherwise, increments the 191 * used in this Lua state (metatable, registring it). Otherwise, increments the
204 * reference count. 192 * reference count.
205 */ 193 */
206char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode mode_) 194char const* DeepFactory::PushDeepProxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode mode_)
207{ 195{
208 // Check if a proxy already exists 196 // Check if a proxy already exists
209 push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC 197 push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC
@@ -228,24 +216,25 @@ char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode m
228 *proxy = prelude; 216 *proxy = prelude;
229 prelude->m_refcount.fetch_add(1, std::memory_order_relaxed); // one more proxy pointing to this deep data 217 prelude->m_refcount.fetch_add(1, std::memory_order_relaxed); // one more proxy pointing to this deep data
230 218
231 // Get/create metatable for 'idfunc' (in this state) 219 // Get/create metatable for 'factory' (in this state)
232 lua_pushlightuserdata( L, std::bit_cast<void*>(prelude->idfunc)); // DPC proxy idfunc 220 DeepFactory& factory = prelude->m_factory;
221 lua_pushlightuserdata( L, std::bit_cast<void*>(&factory)); // DPC proxy factory
233 get_deep_lookup( L); // DPC proxy metatable? 222 get_deep_lookup( L); // DPC proxy metatable?
234 223
235 if( lua_isnil( L, -1)) // // No metatable yet. 224 if( lua_isnil( L, -1)) // // No metatable yet.
236 { 225 {
237 char const* modname;
238 int oldtop = lua_gettop( L); // DPC proxy nil 226 int oldtop = lua_gettop( L); // DPC proxy nil
239 lua_pop( L, 1); // DPC proxy 227 lua_pop( L, 1); // DPC proxy
240 // 1 - make one and register it 228 // 1 - make one and register it
241 if (mode_ != LookupMode::ToKeeper) 229 if (mode_ != LookupMode::ToKeeper)
242 { 230 {
243 (void) prelude->idfunc( L, DeepOp::Metatable); // DPC proxy metatable 231 factory.createMetatable(L); // DPC proxy metatable
244 if( lua_gettop( L) - oldtop != 0 || !lua_istable( L, -1)) 232 if (lua_gettop(L) - oldtop != 0 || !lua_istable(L, -1))
245 { 233 {
234 // factory didn't push exactly 1 value, or the value it pushed is not a table: ERROR!
246 lua_settop( L, oldtop); // DPC proxy X 235 lua_settop( L, oldtop); // DPC proxy X
247 lua_pop( L, 3); // 236 lua_pop( L, 3); //
248 return "Bad idfunc(eOP_metatable): unexpected pushed value"; 237 return "Bad DeepFactory::createMetatable overload: unexpected pushed value";
249 } 238 }
250 // if the metatable contains a __gc, we will call it from our own 239 // if the metatable contains a __gc, we will call it from our own
251 lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc 240 lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc
@@ -271,22 +260,11 @@ char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode m
271 260
272 // Memorize for later rounds 261 // Memorize for later rounds
273 lua_pushvalue( L, -1); // DPC proxy metatable metatable 262 lua_pushvalue( L, -1); // DPC proxy metatable metatable
274 lua_pushlightuserdata( L, std::bit_cast<void*>(prelude->idfunc)); // DPC proxy metatable metatable idfunc 263 lua_pushlightuserdata(L, std::bit_cast<void*>(&factory)); // DPC proxy metatable metatable factory
275 set_deep_lookup( L); // DPC proxy metatable 264 set_deep_lookup( L); // DPC proxy metatable
276 265
277 // 2 - cause the target state to require the module that exported the idfunc 266 // 2 - cause the target state to require the module that exported the factory
278 // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc 267 if (char const* const modname{ factory.moduleName() }; modname) // we actually got a module name
279 {
280 int oldtop_module = lua_gettop( L);
281 modname = (char const*) prelude->idfunc( L, DeepOp::Module); // DPC proxy metatable
282 // make sure the function pushed nothing on the stack!
283 if( lua_gettop( L) - oldtop_module != 0)
284 {
285 lua_pop( L, 3); //
286 return "Bad idfunc(eOP_module): should not push anything";
287 }
288 }
289 if (nullptr != modname) // we actually got a module name
290 { 268 {
291 // L.registry._LOADED exists without having registered the 'package' library. 269 // L.registry._LOADED exists without having registered the 'package' library.
292 lua_getglobal( L, "require"); // DPC proxy metatable require() 270 lua_getglobal( L, "require"); // DPC proxy metatable require()
@@ -309,7 +287,7 @@ char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode m
309 if( require_result != LUA_OK) 287 if( require_result != LUA_OK)
310 { 288 {
311 // failed, return the error message 289 // failed, return the error message
312 lua_pushfstring( L, "error while requiring '%s' identified by idfunc(eOP_module): ", modname); 290 lua_pushfstring( L, "error while requiring '%s' identified by DeepFactory::moduleName: ", modname);
313 lua_insert( L, -2); // DPC proxy metatable prefix error 291 lua_insert( L, -2); // DPC proxy metatable prefix error
314 lua_concat( L, 2); // DPC proxy metatable error 292 lua_concat( L, 2); // DPC proxy metatable error
315 return lua_tostring( L, -1); 293 return lua_tostring( L, -1);
@@ -323,7 +301,7 @@ char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode m
323 else // no L.registry._LOADED; can this ever happen? 301 else // no L.registry._LOADED; can this ever happen?
324 { 302 {
325 lua_pop( L, 6); // 303 lua_pop( L, 6); //
326 return "unexpected error while requiring a module identified by idfunc(eOP_module)"; 304 return "unexpected error while requiring a module identified by DeepFactory::moduleName";
327 } 305 }
328 } 306 }
329 else // a module name, but no require() function :-( 307 else // a module name, but no require() function :-(
@@ -334,7 +312,7 @@ char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode m
334 } 312 }
335 } 313 }
336 STACK_CHECK(L, 2); // DPC proxy metatable 314 STACK_CHECK(L, 2); // DPC proxy metatable
337 ASSERT_L(lua_type(L, -2) == LUA_TUSERDATA); 315 ASSERT_L(lua_type_as_enum(L, -2) == LuaType::USERDATA);
338 ASSERT_L(lua_istable( L, -1)); 316 ASSERT_L(lua_istable( L, -1));
339 lua_setmetatable( L, -2); // DPC proxy 317 lua_setmetatable( L, -2); // DPC proxy
340 318
@@ -343,7 +321,7 @@ char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode m
343 lua_pushvalue( L, -2); // DPC proxy deep proxy 321 lua_pushvalue( L, -2); // DPC proxy deep proxy
344 lua_rawset( L, -4); // DPC proxy 322 lua_rawset( L, -4); // DPC proxy
345 lua_remove( L, -2); // proxy 323 lua_remove( L, -2); // proxy
346 ASSERT_L(lua_type(L, -1) == LUA_TUSERDATA); 324 ASSERT_L(lua_type_as_enum(L, -1) == LuaType::USERDATA);
347 STACK_CHECK(L, 0); 325 STACK_CHECK(L, 0);
348 return nullptr; 326 return nullptr;
349} 327}
@@ -353,56 +331,47 @@ char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode m
353/* 331/*
354* Create a deep userdata 332* Create a deep userdata
355* 333*
356* proxy_ud= deep_userdata( idfunc [, ...] ) 334* proxy_ud= deep_userdata( [...] )
357*
358* Creates a deep userdata entry of the type defined by 'idfunc'.
359* Parameters found on the stack are left as is passed on to the 'idfunc' "new" invocation.
360*
361* 'idfunc' must fulfill the following features:
362* 335*
363* lightuserdata = idfunc( DeepOp::New [, ...] ) -- creates a new deep data instance 336* Creates a deep userdata entry of the type defined by the factory.
364* void = idfunc( DeepOp::Delete, lightuserdata ) -- releases a deep data instance 337* Parameters found on the stack are left as is and passed on to DeepFactory::newDeepObjectInternal.
365* tbl = idfunc( DeepOp::Metatable ) -- gives metatable for userdata proxies
366* 338*
367* Reference counting and true userdata proxying are taken care of for the 339* Reference counting and true userdata proxying are taken care of for the actual data type.
368* actual data type.
369* 340*
370* Types using the deep userdata system (and only those!) can be passed between 341* Types using the deep userdata system (and only those!) can be passed between
371* separate Lua states via 'luaG_inter_move()'. 342* separate Lua states via 'luaG_inter_move()'.
372* 343*
373* Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()' 344* Returns: 'proxy' userdata for accessing the deep data via 'DeepFactory::toDeep()'
374*/ 345*/
375int luaG_newdeepuserdata(Dest L, luaG_IdFunction idfunc, int nuv_) 346int DeepFactory::pushDeepUserdata(Dest L, int nuv_) const
376{ 347{
377 STACK_GROW( L, 1); 348 STACK_GROW( L, 1);
378 STACK_CHECK_START_REL(L, 0); 349 STACK_CHECK_START_REL(L, 0);
379 int const oldtop{ lua_gettop(L) }; 350 int const oldtop{ lua_gettop(L) };
380 DeepPrelude* const prelude{ static_cast<DeepPrelude*>(idfunc(L, DeepOp::New)) }; 351 DeepPrelude* const prelude{ newDeepObjectInternal(L) };
381 if (prelude == nullptr) 352 if (prelude == nullptr)
382 { 353 {
383 return luaL_error( L, "idfunc(DeepOp::New) failed to create deep userdata (out of memory)"); 354 return luaL_error( L, "DeepFactory::newDeepObjectInternal failed to create deep userdata (out of memory)");
384 } 355 }
385 356
386 if( prelude->magic != DEEP_VERSION) 357 if( prelude->m_magic != DEEP_VERSION)
387 { 358 {
388 // just in case, don't leak the newly allocated deep userdata object 359 // just in case, don't leak the newly allocated deep userdata object
389 lua_pushlightuserdata( L, prelude); 360 deleteDeepObjectInternal(L, prelude);
390 idfunc( L, DeepOp::Delete); 361 return luaL_error( L, "Bad Deep Factory: DEEP_VERSION is incorrect, rebuild your implementation with the latest deep implementation");
391 return luaL_error( L, "Bad idfunc(DeepOp::New): DEEP_VERSION is incorrect, rebuild your implementation with the latest deep implementation");
392 } 362 }
393 363
394 ASSERT_L(prelude->m_refcount.load(std::memory_order_relaxed) == 0); // 'push_deep_proxy' will lift it to 1 364 ASSERT_L(prelude->m_refcount.load(std::memory_order_relaxed) == 0); // 'DeepFactory::PushDeepProxy' will lift it to 1
395 prelude->idfunc = idfunc; 365 ASSERT_L(&prelude->m_factory == this);
396 366
397 if( lua_gettop( L) - oldtop != 0) 367 if( lua_gettop( L) - oldtop != 0)
398 { 368 {
399 // just in case, don't leak the newly allocated deep userdata object 369 // just in case, don't leak the newly allocated deep userdata object
400 lua_pushlightuserdata( L, prelude); 370 deleteDeepObjectInternal(L, prelude);
401 idfunc( L, DeepOp::Delete); 371 return luaL_error(L, "Bad DeepFactory::newDeepObjectInternal overload: should not push anything on the stack");
402 return luaL_error( L, "Bad idfunc(DeepOp::New): should not push anything on the stack");
403 } 372 }
404 373
405 char const* const errmsg{ push_deep_proxy(L, prelude, nuv_, LookupMode::LaneBody) }; // proxy 374 char const* const errmsg{ DeepFactory::PushDeepProxy(L, prelude, nuv_, LookupMode::LaneBody) }; // proxy
406 if (errmsg != nullptr) 375 if (errmsg != nullptr)
407 { 376 {
408 return luaL_error( L, errmsg); 377 return luaL_error( L, errmsg);
@@ -419,11 +388,11 @@ int luaG_newdeepuserdata(Dest L, luaG_IdFunction idfunc, int nuv_)
419* Reference count is not changed, and access to the deep userdata is not 388* Reference count is not changed, and access to the deep userdata is not
420* serialized. It is the module's responsibility to prevent conflicting usage. 389* serialized. It is the module's responsibility to prevent conflicting usage.
421*/ 390*/
422DeepPrelude* luaG_todeep(lua_State* L, luaG_IdFunction idfunc, int index) 391DeepPrelude* DeepFactory::toDeep(lua_State* L, int index) const
423{ 392{
424 STACK_CHECK_START_REL(L, 0); 393 STACK_CHECK_START_REL(L, 0);
425 // ensure it is actually a deep userdata 394 // ensure it is actually a deep userdata we created
426 if (get_idfunc(L, index, LookupMode::LaneBody) != idfunc) 395 if (get_factory(L, index, LookupMode::LaneBody) != this)
427 { 396 {
428 return nullptr; // no metatable, or wrong kind 397 return nullptr; // no metatable, or wrong kind
429 } 398 }
@@ -444,8 +413,8 @@ DeepPrelude* luaG_todeep(lua_State* L, luaG_IdFunction idfunc, int index)
444 */ 413 */
445bool copydeep(Universe* U, Dest L2, int L2_cache_i, Source L, int i, LookupMode mode_, char const* upName_) 414bool copydeep(Universe* U, Dest L2, int L2_cache_i, Source L, int i, LookupMode mode_, char const* upName_)
446{ 415{
447 luaG_IdFunction const idfunc { get_idfunc(L, i, mode_) }; 416 DeepFactory* const factory { get_factory(L, i, mode_) };
448 if (idfunc == nullptr) 417 if (factory == nullptr)
449 { 418 {
450 return false; // not a deep userdata 419 return false; // not a deep userdata
451 } 420 }
@@ -463,7 +432,7 @@ bool copydeep(Universe* U, Dest L2, int L2_cache_i, Source L, int i, LookupMode
463 lua_pop( L, 1); // ... u [uv]* 432 lua_pop( L, 1); // ... u [uv]*
464 STACK_CHECK( L, nuv); 433 STACK_CHECK( L, nuv);
465 434
466 char const* errmsg{ push_deep_proxy(L2, *lua_tofulluserdata<DeepPrelude*>(L, i), nuv, mode_) }; // u 435 char const* errmsg{ DeepFactory::PushDeepProxy(L2, *lua_tofulluserdata<DeepPrelude*>(L, i), nuv, mode_) }; // u
467 436
468 // transfer all uservalues of the source in the destination 437 // transfer all uservalues of the source in the destination
469 { 438 {
diff --git a/src/deep.h b/src/deep.h
index 7be5c5d..7c0aa6d 100644
--- a/src/deep.h
+++ b/src/deep.h
@@ -28,34 +28,60 @@ enum class LookupMode
28 FromKeeper // send a function from a keeper state to a lane 28 FromKeeper // send a function from a keeper state to a lane
29}; 29};
30 30
31enum class DeepOp
32{
33 New,
34 Delete,
35 Metatable,
36 Module,
37};
38
39using luaG_IdFunction = void*(*)(lua_State* L, DeepOp op_);
40
41// ################################################################################################ 31// ################################################################################################
42 32
43// xxh64 of string "DEEP_VERSION_3" generated at https://www.pelock.com/products/hash-calculator 33// xxh64 of string "DEEP_VERSION_3" generated at https://www.pelock.com/products/hash-calculator
44static constexpr UniqueKey DEEP_VERSION{ 0xB2CC0FD9C0AE9674ull }; 34static constexpr UniqueKey DEEP_VERSION{ 0xB2CC0FD9C0AE9674ull, "DEEP_VERSION_3" };
45 35
46// should be used as header for deep userdata 36// should be used as header for deep userdata
47// a deep userdata is a full userdata that stores a single pointer to the actual DeepPrelude-derived object 37// a deep userdata is a full userdata that stores a single pointer to the actual DeepPrelude-derived object
48struct DeepPrelude 38struct DeepPrelude
49{ 39{
50 UniqueKey const magic{ DEEP_VERSION }; 40 UniqueKey const m_magic{ DEEP_VERSION };
51 // when stored in a keeper state, the full userdata doesn't have a metatable, so we need direct access to the idfunc 41 // when stored in a keeper state, the full userdata doesn't have a metatable, so we need direct access to the factory
52 luaG_IdFunction idfunc { nullptr }; 42 class DeepFactory& m_factory;
53 // data is destroyed when refcount is 0 43 // data is destroyed when refcount is 0
54 std::atomic<int> m_refcount{ 0 }; 44 std::atomic<int> m_refcount{ 0 };
45
46 DeepPrelude(DeepFactory& factory_)
47 : m_factory{ factory_ }
48 {
49 }
55}; 50};
56 51
57[[nodiscard]] char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode mode_); 52// external C modules should create a single object implementing that interface for each Deep userdata class they want to expose
58void free_deep_prelude(lua_State* L, DeepPrelude* prelude_); 53class DeepFactory
54{
55 protected:
56
57 // protected non-virtual destructor: Lanes won't manage the Factory's lifetime
58 DeepFactory() = default;
59 ~DeepFactory() = default;
60
61 public:
62
63 // non-copyable, non-movable
64 DeepFactory(DeepFactory const&) = delete;
65 DeepFactory(DeepFactory const&&) = delete;
66 DeepFactory& operator=(DeepFactory const&) = delete;
67 DeepFactory& operator=(DeepFactory const&&) = delete;
68
69 private:
70
71 // NVI: private overrides
72 virtual DeepPrelude* newDeepObjectInternal(lua_State* L) const = 0;
73 virtual void deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) const = 0;
74 virtual void createMetatable(lua_State* L) const = 0;
75 virtual char const* moduleName() const = 0;
76
77 public:
78
79 // NVI: public interface
80 int pushDeepUserdata(Dest L, int nuv_) const;
81 DeepPrelude* toDeep(lua_State* L, int index) const;
82 static void DeleteDeepObject(lua_State* L, DeepPrelude* o_);
83 static char const* PushDeepProxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode mode_);
84};
59 85
60LANES_API [[nodiscard]] int luaG_newdeepuserdata(Dest L, luaG_IdFunction idfunc, int nuv_); 86//LANES_API [[nodiscard]] int luaG_newdeepuserdata(Dest L, DeepFactory& factory_, int nuv_);
61LANES_API [[nodiscard]] DeepPrelude* luaG_todeep(lua_State* L, luaG_IdFunction idfunc, int index); 87//LANES_API [[nodiscard]] DeepPrelude* luaG_todeep(lua_State* L, DeepFactory &factory_, int index);
diff --git a/src/keeper.cpp b/src/keeper.cpp
index 61321e1..36733e3 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -833,6 +833,8 @@ KeeperCallResult keeper_call(Universe* U, lua_State* K, keeper_api_t func_, lua_
833 KeeperCallResult result; 833 KeeperCallResult result;
834 int const args{ starting_index ? (lua_gettop(L) - starting_index + 1) : 0 }; 834 int const args{ starting_index ? (lua_gettop(L) - starting_index + 1) : 0 };
835 int const top_K{ lua_gettop(K) }; 835 int const top_K{ lua_gettop(K) };
836 // if we didn't do anything wrong, the keeper stack should be clean
837 ASSERT_L(lua_gettop(K) == 0);
836 838
837 STACK_GROW(K, 2); 839 STACK_GROW(K, 2);
838 840
diff --git a/src/keeper.h b/src/keeper.h
index 7ec8b15..c1ee244 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -34,7 +34,7 @@ struct Keepers
34 34
35static constexpr uintptr_t KEEPER_MAGIC_SHIFT{ 3 }; 35static constexpr uintptr_t KEEPER_MAGIC_SHIFT{ 3 };
36// crc64/we of string "NIL_SENTINEL" generated at http://www.nitrxgen.net/hashgen/ 36// crc64/we of string "NIL_SENTINEL" generated at http://www.nitrxgen.net/hashgen/
37static constexpr UniqueKey NIL_SENTINEL{ 0x7eaafa003a1d11a1ull, "internal nil sentinel" }; 37static constexpr UniqueKey NIL_SENTINEL{ 0x7eaafa003a1d11a1ull, "linda.null" };
38 38
39void init_keepers(Universe* U, lua_State* L); 39void init_keepers(Universe* U, lua_State* L);
40void close_keepers(Universe* U); 40void close_keepers(Universe* U);
diff --git a/src/lanes.cpp b/src/lanes.cpp
index 1f795cc..d9262cf 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -544,14 +544,12 @@ static void selfdestruct_add(Lane* lane_)
544 } 544 }
545 } 545 }
546 546
547 // necessary so that calling free_deep_prelude doesn't crash because linda_id expects a linda lightuserdata at absolute slot 1
548 lua_settop(L, 0);
549 // no need to mutex-protect this as all threads in the universe are gone at that point 547 // no need to mutex-protect this as all threads in the universe are gone at that point
550 if (U->timer_deep != nullptr) // test ins case some early internal error prevented Lanes from creating the deep timer 548 if (U->timer_deep != nullptr) // test ins case some early internal error prevented Lanes from creating the deep timer
551 { 549 {
552 [[maybe_unused]] int const prev_ref_count{ U->timer_deep->m_refcount.fetch_sub(1, std::memory_order_relaxed) }; 550 [[maybe_unused]] int const prev_ref_count{ U->timer_deep->m_refcount.fetch_sub(1, std::memory_order_relaxed) };
553 ASSERT_L(prev_ref_count == 1); // this should be the last reference 551 ASSERT_L(prev_ref_count == 1); // this should be the last reference
554 free_deep_prelude(L, U->timer_deep); 552 DeepFactory::DeleteDeepObject(L, U->timer_deep);
555 U->timer_deep = nullptr; 553 U->timer_deep = nullptr;
556 } 554 }
557 555
@@ -1840,7 +1838,7 @@ LUAG_FUNC(configure)
1840 STACK_CHECK(L, 2); 1838 STACK_CHECK(L, 2);
1841 1839
1842 { 1840 {
1843 char const* errmsg{ push_deep_proxy(Dest{ L }, U->timer_deep, 0, LookupMode::LaneBody) }; // settings M timer_deep 1841 char const* errmsg{ DeepFactory::PushDeepProxy(Dest{ L }, U->timer_deep, 0, LookupMode::LaneBody) }; // settings M timer_deep
1844 if (errmsg != nullptr) 1842 if (errmsg != nullptr)
1845 { 1843 {
1846 return luaL_error(L, errmsg); 1844 return luaL_error(L, errmsg);
diff --git a/src/linda.cpp b/src/linda.cpp
index dbd6b21..103f4ed 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -45,6 +45,18 @@ THE SOFTWARE.
45// xxh64 of string "CANCEL_ERROR" generated at https://www.pelock.com/products/hash-calculator 45// xxh64 of string "CANCEL_ERROR" generated at https://www.pelock.com/products/hash-calculator
46static constexpr UniqueKey BATCH_SENTINEL{ 0x2DDFEE0968C62AA7ull, "linda.batched" }; 46static constexpr UniqueKey BATCH_SENTINEL{ 0x2DDFEE0968C62AA7ull, "linda.batched" };
47 47
48class LindaFactory : public DeepFactory
49{
50 private:
51
52 DeepPrelude* newDeepObjectInternal(lua_State* L) const override;
53 void deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) const override;
54 void createMetatable(lua_State* L) const override;
55 char const* moduleName() const override;
56};
57// I'm not totally happy with having a global variable. But since it's stateless, it will do for the time being.
58static LindaFactory g_LindaFactory;
59
48/* 60/*
49* Actual data is kept within a keeper state, which is hashed by the 'Linda' 61* Actual data is kept within a keeper state, which is hashed by the 'Linda'
50* pointer (which is same to all userdatas pointing to it). 62* pointer (which is same to all userdatas pointing to it).
@@ -82,7 +94,8 @@ class Linda : public DeepPrelude // Deep userdata MUST start with this header
82 static void operator delete(void* p_) { static_cast<Linda*>(p_)->U->internal_allocator.free(p_, sizeof(Linda)); } 94 static void operator delete(void* p_) { static_cast<Linda*>(p_)->U->internal_allocator.free(p_, sizeof(Linda)); }
83 95
84 Linda(Universe* U_, uintptr_t group_, char const* name_, size_t len_) 96 Linda(Universe* U_, uintptr_t group_, char const* name_, size_t len_)
85 : U{ U_ } 97 : DeepPrelude{ g_LindaFactory }
98 , U{ U_ }
86 , group{ group_ << KEEPER_MAGIC_SHIFT } 99 , group{ group_ << KEEPER_MAGIC_SHIFT }
87 { 100 {
88 setName(name_, len_); 101 setName(name_, len_);
@@ -141,12 +154,13 @@ class Linda : public DeepPrelude // Deep userdata MUST start with this header
141 return nullptr; 154 return nullptr;
142 } 155 }
143}; 156};
144[[nodiscard]] static void* linda_id(lua_State*, DeepOp);
145 157
146template<bool OPT> 158// #################################################################################################
159
160template <bool OPT>
147[[nodiscard]] static inline Linda* ToLinda(lua_State* L, int idx_) 161[[nodiscard]] static inline Linda* ToLinda(lua_State* L, int idx_)
148{ 162{
149 Linda* const linda{ static_cast<Linda*>(luaG_todeep(L, linda_id, idx_)) }; 163 Linda* const linda{ static_cast<Linda*>(g_LindaFactory.toDeep(L, idx_)) };
150 if constexpr (!OPT) 164 if constexpr (!OPT)
151 { 165 {
152 luaL_argcheck(L, linda != nullptr, idx_, "expecting a linda object"); 166 luaL_argcheck(L, linda != nullptr, idx_, "expecting a linda object");
@@ -171,7 +185,6 @@ static void check_key_types(lua_State* L, int start_, int end_)
171 185
172 case LuaType::LIGHTUSERDATA: 186 case LuaType::LIGHTUSERDATA:
173 { 187 {
174 // NIL_SENTINEL isn't publicly exposed, but it doesn't hurt to check
175 static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> to_check{ BATCH_SENTINEL, CANCEL_ERROR, NIL_SENTINEL }; 188 static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> to_check{ BATCH_SENTINEL, CANCEL_ERROR, NIL_SENTINEL };
176 for (UniqueKey const& key : to_check) 189 for (UniqueKey const& key : to_check)
177 { 190 {
@@ -199,12 +212,16 @@ LUAG_FUNC(linda_protected_call)
199 lua_State* const KL{ K ? K->L : nullptr }; 212 lua_State* const KL{ K ? K->L : nullptr };
200 if (KL == nullptr) 213 if (KL == nullptr)
201 return 0; 214 return 0;
215 // if we didn't do anything wrong, the keeper stack should be clean
216 ASSERT_L(lua_gettop(KL) == 0);
202 217
203 // retrieve the actual function to be called and move it before the arguments 218 // retrieve the actual function to be called and move it before the arguments
204 lua_pushvalue(L, lua_upvalueindex(1)); 219 lua_pushvalue(L, lua_upvalueindex(1));
205 lua_insert(L, 1); 220 lua_insert(L, 1);
206 // do a protected call 221 // do a protected call
207 int const rc{ lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) }; 222 int const rc{ lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) };
223 // whatever happens, the keeper state stack must be empty when we are done
224 lua_settop(KL, 0);
208 225
209 // release the keeper 226 // release the keeper
210 keeper_release(K); 227 keeper_release(K);
@@ -838,179 +855,152 @@ LUAG_FUNC(linda_towatch)
838 855
839// ################################################################################################# 856// #################################################################################################
840 857
841/* 858DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* L) const
842* Identity function of a shared userdata object.
843*
844* lightuserdata= linda_id( "new" [, ...] )
845* = linda_id( "delete", lightuserdata )
846*
847* Creation and cleanup of actual 'deep' objects. 'luaG_...' will wrap them into
848* regular userdata proxies, per each state using the deep data.
849*
850* tbl= linda_id( "metatable" )
851*
852* Returns a metatable for the proxy objects ('__gc' method not needed; will
853* be added by 'luaG_...')
854*
855* string= linda_id( "module")
856*
857* Returns the name of the module that a state should require
858* in order to keep a handle on the shared library that exported the idfunc
859*
860* = linda_id( str, ... )
861*
862* For any other strings, the ID function must not react at all. This allows
863* future extensions of the system.
864*/
865[[nodiscard]] static void* linda_id(lua_State* L, DeepOp op_)
866{ 859{
867 switch( op_) 860 size_t name_len = 0;
861 char const* linda_name = nullptr;
862 unsigned long linda_group = 0;
863 // should have a string and/or a number of the stack as parameters (name and group)
864 switch (lua_gettop(L))
868 { 865 {
869 case DeepOp::New: 866 default: // 0
867 break;
868
869 case 1: // 1 parameter, either a name or a group
870 if (lua_type(L, -1) == LUA_TSTRING)
870 { 871 {
871 size_t name_len = 0; 872 linda_name = lua_tolstring(L, -1, &name_len);
872 char const* linda_name = nullptr; 873 }
873 unsigned long linda_group = 0; 874 else
874 // should have a string and/or a number of the stack as parameters (name and group) 875 {
875 switch (lua_gettop(L)) 876 linda_group = (unsigned long) lua_tointeger(L, -1);
876 { 877 }
877 default: // 0 878 break;
878 break;
879 879
880 case 1: // 1 parameter, either a name or a group 880 case 2: // 2 parameters, a name and group, in that order
881 if (lua_type(L, -1) == LUA_TSTRING) 881 linda_name = lua_tolstring(L, -2, &name_len);
882 { 882 linda_group = (unsigned long) lua_tointeger(L, -1);
883 linda_name = lua_tolstring(L, -1, &name_len); 883 break;
884 } 884 }
885 else
886 {
887 linda_group = (unsigned long) lua_tointeger(L, -1);
888 }
889 break;
890 885
891 case 2: // 2 parameters, a name and group, in that order 886 /* The deep data is allocated separately of Lua stack; we might no
892 linda_name = lua_tolstring(L, -2, &name_len); 887 * longer be around when last reference to it is being released.
893 linda_group = (unsigned long) lua_tointeger(L, -1); 888 * One can use any memory allocation scheme.
894 break; 889 * just don't use L's allocF because we don't know which state will get the honor of GCing the linda
895 } 890 */
891 Universe* const U{ universe_get(L) };
892 Linda* linda{ new (U) Linda{ U, linda_group, linda_name, name_len } };
893 return linda;
894}
896 895
897 /* The deep data is allocated separately of Lua stack; we might no 896// #################################################################################################
898 * longer be around when last reference to it is being released.
899 * One can use any memory allocation scheme.
900 * just don't use L's allocF because we don't know which state will get the honor of GCing the linda
901 */
902 Universe* const U{ universe_get(L) };
903 Linda* linda{ new (U) Linda{ U, linda_group, linda_name, name_len } };
904 return linda;
905 }
906 897
907 case DeepOp::Delete: 898void LindaFactory::deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) const
899{
900 Linda* const linda{ static_cast<Linda*>(o_) };
901 ASSERT_L(linda);
902 Keeper* const myK{ which_keeper(linda->U->keepers, linda->hashSeed()) };
903 // if collected after the universe, keepers are already destroyed, and there is nothing to clear
904 if (myK)
905 {
906 // if collected from my own keeper, we can't acquire/release it
907 // because we are already inside a protected area, and trying to do so would deadlock!
908 bool const need_acquire_release{ myK->L != L };
909 // Clean associated structures in the keeper state.
910 Keeper* const K{ need_acquire_release ? keeper_acquire(linda->U->keepers, linda->hashSeed()) : myK };
911 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex...
912 [[maybe_unused]] KeeperCallResult const result{ keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0) };
913 ASSERT_L(result.has_value() && result.value() == 0);
914 if (need_acquire_release)
908 { 915 {
909 Linda* const linda{ lua_tolightuserdata<Linda>(L, 1) }; 916 keeper_release(K);
910 ASSERT_L(linda);
911 Keeper* const myK{ which_keeper(linda->U->keepers, linda->hashSeed()) };
912 // if collected after the universe, keepers are already destroyed, and there is nothing to clear
913 if (myK)
914 {
915 // if collected from my own keeper, we can't acquire/release it
916 // because we are already inside a protected area, and trying to do so would deadlock!
917 bool const need_acquire_release{ myK->L != L };
918 // Clean associated structures in the keeper state.
919 Keeper* const K{ need_acquire_release ? keeper_acquire(linda->U->keepers, linda->hashSeed()) : myK };
920 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex...
921 [[maybe_unused]] KeeperCallResult const result{ keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0) };
922 ASSERT_L(result.has_value() && result.value() == 0);
923 if (need_acquire_release)
924 {
925 keeper_release(K);
926 }
927 }
928
929 delete linda; // operator delete overload ensures things go as expected
930 return nullptr;
931 } 917 }
918 }
932 919
933 case DeepOp::Metatable: 920 delete linda; // operator delete overload ensures things go as expected
934 { 921}
935 STACK_CHECK_START_REL(L, 0);
936 lua_newtable(L);
937 // metatable is its own index
938 lua_pushvalue(L, -1);
939 lua_setfield(L, -2, "__index");
940 922
941 // protect metatable from external access 923// #################################################################################################
942 lua_pushliteral(L, "Linda");
943 lua_setfield(L, -2, "__metatable");
944 924
945 lua_pushcfunction(L, LG_linda_tostring); 925void LindaFactory::createMetatable(lua_State* L) const
946 lua_setfield(L, -2, "__tostring"); 926{
927 STACK_CHECK_START_REL(L, 0);
928 lua_newtable(L);
929 // metatable is its own index
930 lua_pushvalue(L, -1);
931 lua_setfield(L, -2, "__index");
947 932
948 // Decoda __towatch support 933 // protect metatable from external access
949 lua_pushcfunction(L, LG_linda_towatch); 934 lua_pushliteral(L, "Linda");
950 lua_setfield(L, -2, "__towatch"); 935 lua_setfield(L, -2, "__metatable");
951 936
952 lua_pushcfunction(L, LG_linda_concat); 937 lua_pushcfunction(L, LG_linda_tostring);
953 lua_setfield(L, -2, "__concat"); 938 lua_setfield(L, -2, "__tostring");
954 939
955 // protected calls, to ensure associated keeper is always released even in case of error 940 // Decoda __towatch support
956 // all function are the protected call wrapper, where the actual operation is provided as upvalue 941 lua_pushcfunction(L, LG_linda_towatch);
957 // note that this kind of thing can break function lookup as we use the function pointer here and there 942 lua_setfield(L, -2, "__towatch");
958 // TODO: change that and use different functions!
959 943
960 lua_pushcfunction(L, LG_linda_send); 944 lua_pushcfunction(L, LG_linda_concat);
961 lua_pushcclosure(L, LG_linda_protected_call, 1); 945 lua_setfield(L, -2, "__concat");
962 lua_setfield(L, -2, "send");
963 946
964 lua_pushcfunction(L, LG_linda_receive); 947 // protected calls, to ensure associated keeper is always released even in case of error
965 lua_pushcclosure(L, LG_linda_protected_call, 1); 948 // all function are the protected call wrapper, where the actual operation is provided as upvalue
966 lua_setfield(L, -2, "receive"); 949 // note that this kind of thing can break function lookup as we use the function pointer here and there
950 // TODO: change that and use different functions!
967 951
968 lua_pushcfunction(L, LG_linda_limit); 952 lua_pushcfunction(L, LG_linda_send);
969 lua_pushcclosure(L, LG_linda_protected_call, 1); 953 lua_pushcclosure(L, LG_linda_protected_call, 1);
970 lua_setfield(L, -2, "limit"); 954 lua_setfield(L, -2, "send");
971 955
972 lua_pushcfunction(L, LG_linda_set); 956 lua_pushcfunction(L, LG_linda_receive);
973 lua_pushcclosure(L, LG_linda_protected_call, 1); 957 lua_pushcclosure(L, LG_linda_protected_call, 1);
974 lua_setfield(L, -2, "set"); 958 lua_setfield(L, -2, "receive");
975 959
976 lua_pushcfunction(L, LG_linda_count); 960 lua_pushcfunction(L, LG_linda_limit);
977 lua_pushcclosure(L, LG_linda_protected_call, 1); 961 lua_pushcclosure(L, LG_linda_protected_call, 1);
978 lua_setfield(L, -2, "count"); 962 lua_setfield(L, -2, "limit");
979 963
980 lua_pushcfunction(L, LG_linda_get); 964 lua_pushcfunction(L, LG_linda_set);
981 lua_pushcclosure(L, LG_linda_protected_call, 1); 965 lua_pushcclosure(L, LG_linda_protected_call, 1);
982 lua_setfield(L, -2, "get"); 966 lua_setfield(L, -2, "set");
983 967
984 lua_pushcfunction(L, LG_linda_cancel); 968 lua_pushcfunction(L, LG_linda_count);
985 lua_setfield(L, -2, "cancel"); 969 lua_pushcclosure(L, LG_linda_protected_call, 1);
970 lua_setfield(L, -2, "count");
986 971
987 lua_pushcfunction(L, LG_linda_deep); 972 lua_pushcfunction(L, LG_linda_get);
988 lua_setfield(L, -2, "deep"); 973 lua_pushcclosure(L, LG_linda_protected_call, 1);
974 lua_setfield(L, -2, "get");
989 975
990 lua_pushcfunction(L, LG_linda_dump); 976 lua_pushcfunction(L, LG_linda_cancel);
991 lua_pushcclosure(L, LG_linda_protected_call, 1); 977 lua_setfield(L, -2, "cancel");
992 lua_setfield(L, -2, "dump");
993 978
994 // some constants 979 lua_pushcfunction(L, LG_linda_deep);
995 BATCH_SENTINEL.pushKey(L); 980 lua_setfield(L, -2, "deep");
996 lua_setfield(L, -2, "batched");
997 981
998 NIL_SENTINEL.pushKey(L); 982 lua_pushcfunction(L, LG_linda_dump);
999 lua_setfield(L, -2, "null"); 983 lua_pushcclosure(L, LG_linda_protected_call, 1);
984 lua_setfield(L, -2, "dump");
1000 985
1001 STACK_CHECK(L, 1); 986 // some constants
1002 return nullptr; 987 BATCH_SENTINEL.pushKey(L);
1003 } 988 lua_setfield(L, -2, "batched");
1004 989
1005 case DeepOp::Module: 990 NIL_SENTINEL.pushKey(L);
1006 // linda is a special case because we know lanes must be loaded from the main lua state 991 lua_setfield(L, -2, "null");
1007 // to be able to ever get here, so we know it will remain loaded as long a the main state is around 992
1008 // in other words, forever. 993 STACK_CHECK(L, 1);
1009 default: 994}
1010 { 995
1011 return nullptr; 996// #################################################################################################
1012 } 997
1013 } 998char const* LindaFactory::moduleName() const
999{
1000 // linda is a special case because we know lanes must be loaded from the main lua state
1001 // to be able to ever get here, so we know it will remain loaded as long a the main state is around
1002 // in other words, forever.
1003 return nullptr;
1014} 1004}
1015 1005
1016// ################################################################################################# 1006// #################################################################################################
@@ -1034,5 +1024,5 @@ LUAG_FUNC(linda)
1034 luaL_checktype(L, 1, LUA_TSTRING); 1024 luaL_checktype(L, 1, LUA_TSTRING);
1035 luaL_checktype(L, 2, LUA_TNUMBER); 1025 luaL_checktype(L, 2, LUA_TNUMBER);
1036 } 1026 }
1037 return luaG_newdeepuserdata(Dest{ L }, linda_id, 0); 1027 return g_LindaFactory.pushDeepUserdata(Dest{ L }, 0);
1038} 1028}