diff options
-rw-r--r-- | deep_test/deep_test.cpp | 66 | ||||
-rw-r--r-- | deep_test/deep_test.vcxproj | 14 | ||||
-rw-r--r-- | docs/index.html | 39 | ||||
-rw-r--r-- | src/deep.cpp | 149 | ||||
-rw-r--r-- | src/deep.h | 62 | ||||
-rw-r--r-- | src/keeper.cpp | 2 | ||||
-rw-r--r-- | src/keeper.h | 2 | ||||
-rw-r--r-- | src/lanes.cpp | 6 | ||||
-rw-r--r-- | src/linda.cpp | 292 |
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 | ||
8 | class 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 | |||
21 | static 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_) | 33 | DeepPrelude* 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: | 41 | void 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[] = | |||
115 | int luaD_new_deep( lua_State* L) | 110 | int 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: | 1688 | class 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 | |||
1698 | static 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/ |
57 | static constexpr UniqueKey DEEP_LOOKUP_KEY{ 0x9fb9b4f3f633d83dull }; | 57 | static 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 | */ |
90 | static void get_deep_lookup(lua_State* L) | 90 | static 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 | ||
144 | void free_deep_prelude(lua_State* L, DeepPrelude* prelude_) | 144 | void 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 | */ |
206 | char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode mode_) | 194 | char 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 | */ |
375 | int luaG_newdeepuserdata(Dest L, luaG_IdFunction idfunc, int nuv_) | 346 | int 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 | */ |
422 | DeepPrelude* luaG_todeep(lua_State* L, luaG_IdFunction idfunc, int index) | 391 | DeepPrelude* 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 | */ |
445 | bool copydeep(Universe* U, Dest L2, int L2_cache_i, Source L, int i, LookupMode mode_, char const* upName_) | 414 | bool 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 | { |
@@ -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 | ||
31 | enum class DeepOp | ||
32 | { | ||
33 | New, | ||
34 | Delete, | ||
35 | Metatable, | ||
36 | Module, | ||
37 | }; | ||
38 | |||
39 | using 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 |
44 | static constexpr UniqueKey DEEP_VERSION{ 0xB2CC0FD9C0AE9674ull }; | 34 | static 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 |
48 | struct DeepPrelude | 38 | struct 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 |
58 | void free_deep_prelude(lua_State* L, DeepPrelude* prelude_); | 53 | class 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 | ||
60 | LANES_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_); |
61 | LANES_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 | ||
35 | static constexpr uintptr_t KEEPER_MAGIC_SHIFT{ 3 }; | 35 | static 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/ |
37 | static constexpr UniqueKey NIL_SENTINEL{ 0x7eaafa003a1d11a1ull, "internal nil sentinel" }; | 37 | static constexpr UniqueKey NIL_SENTINEL{ 0x7eaafa003a1d11a1ull, "linda.null" }; |
38 | 38 | ||
39 | void init_keepers(Universe* U, lua_State* L); | 39 | void init_keepers(Universe* U, lua_State* L); |
40 | void close_keepers(Universe* U); | 40 | void 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 |
46 | static constexpr UniqueKey BATCH_SENTINEL{ 0x2DDFEE0968C62AA7ull, "linda.batched" }; | 46 | static constexpr UniqueKey BATCH_SENTINEL{ 0x2DDFEE0968C62AA7ull, "linda.batched" }; |
47 | 47 | ||
48 | class 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. | ||
58 | static 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 | ||
146 | template<bool OPT> | 158 | // ################################################################################################# |
159 | |||
160 | template <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 | /* | 858 | DeepPrelude* 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: | 898 | void 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); | 925 | void 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 | } | 998 | char 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 | } |