diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-28 18:01:55 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-28 18:01:55 +0200 |
commit | 92944be3c3718095efa38e2a8db94844b1c7f739 (patch) | |
tree | e050cc4e493f6bc69be6b00b9b0562295ba5b123 | |
parent | 8d7791f3eb1c5fc449490845254b59fdde30e9e0 (diff) | |
download | lanes-92944be3c3718095efa38e2a8db94844b1c7f739.tar.gz lanes-92944be3c3718095efa38e2a8db94844b1c7f739.tar.bz2 lanes-92944be3c3718095efa38e2a8db94844b1c7f739.zip |
New Lanes finalizer API lanes.finally()
-rw-r--r-- | docs/index.html | 57 | ||||
-rw-r--r-- | src/lanes.cpp | 1 | ||||
-rw-r--r-- | src/lanes.lua | 1 | ||||
-rw-r--r-- | src/state.cpp | 2 | ||||
-rw-r--r-- | src/universe.cpp | 40 | ||||
-rw-r--r-- | src/universe.h | 3 | ||||
-rw-r--r-- | tests/basic.lua | 6 | ||||
-rw-r--r-- | tests/finalizer.lua | 70 |
8 files changed, 126 insertions, 54 deletions
diff --git a/docs/index.html b/docs/index.html index f5a074f..20dccfa 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -336,8 +336,8 @@ | |||
336 | <tt>nil</tt>/<tt>"protected"</tt>/function | 336 | <tt>nil</tt>/<tt>"protected"</tt>/function |
337 | </td> | 337 | </td> |
338 | <td> | 338 | <td> |
339 | If <tt>nil</tt>, Lua states are created with <tt>lua_newstate()</tt> and reuse the allocator from the master state.<br /> | 339 | If <tt>nil</tt>, Lua states are created with <tt>lua_newstate()</tt> and reuse the allocator from the master state.<br/> |
340 | If <tt>"protected"</tt>, The default allocator obtained from <tt>lua_getallocf()</tt> in the master state is wrapped inside a critical section and used in all newly created states.<br /> | 340 | If <tt>"protected"</tt>, The default allocator obtained from <tt>lua_getallocf()</tt> in the master state is wrapped inside a critical section and used in all newly created states.<br/> |
341 | If a <tt>function</tt>, this function is called prior to creating the state. It should return a full userdata containing the following structure: | 341 | If a <tt>function</tt>, this function is called prior to creating the state. It should return a full userdata containing the following structure: |
342 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"> | 342 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"> |
343 | <tr> | 343 | <tr> |
@@ -360,8 +360,8 @@ | |||
360 | </td> | 360 | </td> |
361 | <td> | 361 | <td> |
362 | Controls which allocator is used for Lanes internal allocations (for keeper, linda and lane management). | 362 | Controls which allocator is used for Lanes internal allocations (for keeper, linda and lane management). |
363 | If <tt>"libc"</tt>, Lanes uses <tt>realloc</tt> and <tt>free</tt>.<br /> | 363 | If <tt>"libc"</tt>, Lanes uses <tt>realloc</tt> and <tt>free</tt>.<br/> |
364 | If <tt>"allocator"</tt>, Lanes uses whatever was obtained from the <tt>"allocator"</tt> setting.<br /> | 364 | If <tt>"allocator"</tt>, Lanes uses whatever was obtained from the <tt>"allocator"</tt> setting.<br/> |
365 | This option is mostly useful for embedders that want control all memory allocations, but have issues when Lanes tries to use the Lua State allocator for internal purposes (especially with LuaJIT). | 365 | This option is mostly useful for embedders that want control all memory allocations, but have issues when Lanes tries to use the Lua State allocator for internal purposes (especially with LuaJIT). |
366 | </td> | 366 | </td> |
367 | </tr> | 367 | </tr> |
@@ -455,6 +455,27 @@ | |||
455 | </tr> | 455 | </tr> |
456 | </table> | 456 | </table> |
457 | 457 | ||
458 | <p> | ||
459 | It is also possible to install a function that will be called when Lanes is shutdown (that is, when the first state that required Lanes is closed). | ||
460 | </p> | ||
461 | |||
462 | <p> | ||
463 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"> | ||
464 | <tr> | ||
465 | <td> | ||
466 | <pre> lanes.finally(<some function>|nil)</pre> | ||
467 | </td> | ||
468 | </tr> | ||
469 | </table> | ||
470 | </p> | ||
471 | |||
472 | <p> | ||
473 | An error will be raised if you attempt to do this from inside a lane, or on bad arguments (non-function, or too many arguments).<br/> | ||
474 | Only the last registered finalizer is kept. It can be cleared by passing <tt>nil</tt> or nothing.<br/> | ||
475 | The installed function is called after all free-running lanes are terminated, but before lindas become unusable.<br/> | ||
476 | If an error occurs inside this finalizer, it is silently swallowed, since it happens only during state shutdown, and you can't do anything about it. | ||
477 | </p> | ||
478 | |||
458 | <hr/> | 479 | <hr/> |
459 | <h2 id="creation">Creation</h2> | 480 | <h2 id="creation">Creation</h2> |
460 | 481 | ||
@@ -682,8 +703,7 @@ | |||
682 | </td> | 703 | </td> |
683 | <td>table</td> | 704 | <td>table</td> |
684 | <td> | 705 | <td> |
685 | Sets the globals table for the launched threads. This can be used for giving them constants. The key/value pairs of <tt>table</tt> are transfered in the lane globals after the libraries have been loaded and the modules required. | 706 | Sets the globals table for the launched threads. This can be used for giving them constants. The key/value pairs of <tt>table</tt> are transfered in the lane globals after the libraries have been loaded and the modules required.<br/> |
686 | <br /> | ||
687 | The global values of different lanes are in no manner connected; modifying one will only affect the particular lane. | 707 | The global values of different lanes are in no manner connected; modifying one will only affect the particular lane. |
688 | </td> | 708 | </td> |
689 | </tr> | 709 | </tr> |
@@ -696,8 +716,7 @@ | |||
696 | Lists modules that have to be required in order to be able to transfer functions they exposed. Starting with Lanes 3.0-beta, non-Lua functions are no longer copied by recreating a C closure from a C pointer, but are <a href="#function_notes">searched in lookup tables</a>. | 716 | Lists modules that have to be required in order to be able to transfer functions they exposed. Starting with Lanes 3.0-beta, non-Lua functions are no longer copied by recreating a C closure from a C pointer, but are <a href="#function_notes">searched in lookup tables</a>. |
697 | These tables are built from the modules listed here. <tt>required</tt> must be a list of strings, each one being the name of a module to be required. Each module is required with <tt>require()</tt> before the lanes function is invoked. | 717 | These tables are built from the modules listed here. <tt>required</tt> must be a list of strings, each one being the name of a module to be required. Each module is required with <tt>require()</tt> before the lanes function is invoked. |
698 | So, from the required module's point of view, requiring it manually from inside the lane body or having it required this way doesn't change anything. From the lane body's point of view, the only difference is that a module not creating a global won't be accessible. | 718 | So, from the required module's point of view, requiring it manually from inside the lane body or having it required this way doesn't change anything. From the lane body's point of view, the only difference is that a module not creating a global won't be accessible. |
699 | Therefore, a lane body will also have to require a module manually, but this won't do anything more (see Lua's <tt>require</tt> documentation). | 719 | Therefore, a lane body will also have to require a module manually, but this won't do anything more (see Lua's <tt>require</tt> documentation). <br/> |
700 | <br /> | ||
701 | ATTEMPTING TO TRANSFER A FUNCTION REGISTERED BY A MODULE NOT LISTED HERE WILL RAISE AN ERROR. | 720 | ATTEMPTING TO TRANSFER A FUNCTION REGISTERED BY A MODULE NOT LISTED HERE WILL RAISE AN ERROR. |
702 | </td> | 721 | </td> |
703 | </tr> | 722 | </tr> |
@@ -707,8 +726,8 @@ | |||
707 | </td> | 726 | </td> |
708 | <td>string</td> | 727 | <td>string</td> |
709 | <td> | 728 | <td> |
710 | Sets the error reporting mode. One of <tt>"minimal"</tt> (the default), <tt>"basic"</tt>, <tt>"extended"</tt>.<br /> | 729 | Sets the error reporting mode. One of <tt>"minimal"</tt> (the default), <tt>"basic"</tt>, <tt>"extended"</tt>.<br/> |
711 | <tt>"minimal"</tt> yields only the location of the error.<br /> | 730 | <tt>"minimal"</tt> yields only the location of the error.<br/> |
712 | The other 2 yield a full stack trace, with different amounts of data extracted from the debug infos. See <a href="#results">Results</a>. | 731 | The other 2 yield a full stack trace, with different amounts of data extracted from the debug infos. See <a href="#results">Results</a>. |
713 | </td> | 732 | </td> |
714 | </tr> | 733 | </tr> |
@@ -737,10 +756,8 @@ | |||
737 | <td>integer</td> | 756 | <td>integer</td> |
738 | <td> | 757 | <td> |
739 | The priority of lanes generated in the range -3..+3 (default is 0). | 758 | The priority of lanes generated in the range -3..+3 (default is 0). |
740 | These values are a mapping over the actual priority range of the underlying implementation. | 759 | These values are a mapping over the actual priority range of the underlying implementation.<br/> |
741 | <br /> | 760 | Implementation and dependability of priorities varies by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode.<br/> |
742 | Implementation and dependability of priorities varies by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode. | ||
743 | <br /> | ||
744 | A lane can also change its own thread priority dynamically with <a href="#priority"><tt>lanes.set_thread_priority()</tt></a>. | 761 | A lane can also change its own thread priority dynamically with <a href="#priority"><tt>lanes.set_thread_priority()</tt></a>. |
745 | </td> | 762 | </td> |
746 | </tr> | 763 | </tr> |
@@ -750,8 +767,7 @@ | |||
750 | </td> | 767 | </td> |
751 | <td> table</td> | 768 | <td> table</td> |
752 | <td> | 769 | <td> |
753 | Specifying it when <code>libs_str</code> doesn't cause the <code>package</code> library to be loaded will generate an error. | 770 | Specifying it when <code>libs_str</code> doesn't cause the <code>package</code> library to be loaded will generate an error.<br/> |
754 | <br /> | ||
755 | If not specified, the created lane will receive the current values of <tt>package</tt>. Only <tt>path</tt>, <tt>cpath</tt>, <tt>preload</tt> and <tt>loaders</tt> (Lua 5.1)/<tt>searchers</tt> (Lua 5.2) are transfered. | 771 | If not specified, the created lane will receive the current values of <tt>package</tt>. Only <tt>path</tt>, <tt>cpath</tt>, <tt>preload</tt> and <tt>loaders</tt> (Lua 5.1)/<tt>searchers</tt> (Lua 5.2) are transfered. |
756 | </td> | 772 | </td> |
757 | </tr> | 773 | </tr> |
@@ -1754,12 +1770,9 @@ static MyDeepFactory g_MyDeepFactory; | |||
1754 | </p> | 1770 | </p> |
1755 | 1771 | ||
1756 | <p> | 1772 | <p> |
1757 | 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: | 1773 | 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:<br/> |
1758 | <br /> | 1774 | 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.<br/> |
1759 | 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. | 1775 | 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.<br/> |
1760 | <br /> | ||
1761 | 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. | ||
1762 | <br /> | ||
1763 | 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. | 1776 | 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. |
1764 | </p> | 1777 | </p> |
1765 | 1778 | ||
diff --git a/src/lanes.cpp b/src/lanes.cpp index 0ea0900..109fba4 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp | |||
@@ -598,6 +598,7 @@ namespace { | |||
598 | { "set_thread_affinity", LG_set_thread_affinity }, | 598 | { "set_thread_affinity", LG_set_thread_affinity }, |
599 | { "nameof", luaG_nameof }, | 599 | { "nameof", luaG_nameof }, |
600 | { "register", LG_register }, | 600 | { "register", LG_register }, |
601 | { Universe::kFinally, Universe::InitializeFinalizer }, | ||
601 | { "set_singlethreaded", LG_set_singlethreaded }, | 602 | { "set_singlethreaded", LG_set_singlethreaded }, |
602 | { nullptr, nullptr } | 603 | { nullptr, nullptr } |
603 | }; | 604 | }; |
diff --git a/src/lanes.lua b/src/lanes.lua index e75e840..6616667 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -799,6 +799,7 @@ local configure = function(settings_) | |||
799 | lanes.null = core.null | 799 | lanes.null = core.null |
800 | lanes.require = core.require | 800 | lanes.require = core.require |
801 | lanes.register = core.register | 801 | lanes.register = core.register |
802 | lanes.finally = core.finally | ||
802 | lanes.set_singlethreaded = core.set_singlethreaded | 803 | lanes.set_singlethreaded = core.set_singlethreaded |
803 | lanes.set_thread_affinity = core.set_thread_affinity | 804 | lanes.set_thread_affinity = core.set_thread_affinity |
804 | lanes.set_thread_priority = core.set_thread_priority | 805 | lanes.set_thread_priority = core.set_thread_priority |
diff --git a/src/state.cpp b/src/state.cpp index 5a1d2fb..d9f5499 100644 --- a/src/state.cpp +++ b/src/state.cpp | |||
@@ -209,7 +209,7 @@ static void copy_one_time_settings(Universe* U_, SourceState L1_, DestState L2_) | |||
209 | 209 | ||
210 | // ################################################################################################# | 210 | // ################################################################################################# |
211 | 211 | ||
212 | static constexpr char const* kOnStateCreate{ "on_state_create" }; | 212 | static constexpr char const* kOnStateCreate{ "on_state_create" }; // update lanes.lua if the name changes! |
213 | 213 | ||
214 | void InitializeOnStateCreate(Universe* U_, lua_State* L_) | 214 | void InitializeOnStateCreate(Universe* U_, lua_State* L_) |
215 | { | 215 | { |
diff --git a/src/universe.cpp b/src/universe.cpp index 5794048..3271e70 100644 --- a/src/universe.cpp +++ b/src/universe.cpp | |||
@@ -197,6 +197,28 @@ void Universe::initializeAllocatorFunction(lua_State* L_) | |||
197 | 197 | ||
198 | // ################################################################################################# | 198 | // ################################################################################################# |
199 | 199 | ||
200 | // should be called ONLY from the state that created the universe | ||
201 | int Universe::InitializeFinalizer(lua_State* L_) | ||
202 | { | ||
203 | luaL_argcheck(L_, lua_gettop(L_) <= 1, 1, "too many arguments"); // L_: f? | ||
204 | lua_settop(L_, 1); // L_: f|nil | ||
205 | luaL_argcheck(L_, lua_isnoneornil(L_, 1) || lua_isfunction(L_, 1), 1, "finalizer should be a function"); | ||
206 | |||
207 | // make sure we are only called from the Master Lua State! | ||
208 | kUniverseFullRegKey.pushValue(L_); // L_: f U | ||
209 | if (lua_type_as_enum(L_, -1) != LuaType::USERDATA) { | ||
210 | raise_luaL_error(L_, "lanes.%s called from inside a lane", kFinally); | ||
211 | } | ||
212 | lua_pop(L_, 1); // L_: f | ||
213 | STACK_GROW(L_, 3); | ||
214 | // _R[kFinalizerRegKey] = f | ||
215 | kFinalizerRegKey.setValue(L_, [](lua_State* L_) { lua_insert(L_, -2); }); // L_: | ||
216 | // no need to adjust the stack, Lua does this for us | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | // ################################################################################################# | ||
221 | |||
200 | /* | 222 | /* |
201 | * Initialize keeper states | 223 | * Initialize keeper states |
202 | * | 224 | * |
@@ -373,9 +395,22 @@ int universe_gc(lua_State* L_) | |||
373 | { | 395 | { |
374 | lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; | 396 | lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; |
375 | std::string_view const _op_string{ lua_tostringview(L_, lua_upvalueindex(2)) }; | 397 | std::string_view const _op_string{ lua_tostringview(L_, lua_upvalueindex(2)) }; |
376 | Universe* const _U{ lua_tofulluserdata<Universe>(L_, 1) }; | 398 | STACK_CHECK_START_ABS(L_, 1); |
399 | Universe* const _U{ lua_tofulluserdata<Universe>(L_, 1) }; // L_: U | ||
377 | _U->terminateFreeRunningLanes(L_, _shutdown_timeout, which_cancel_op(_op_string)); | 400 | _U->terminateFreeRunningLanes(L_, _shutdown_timeout, which_cancel_op(_op_string)); |
378 | 401 | ||
402 | // invoke the function installed by lanes.finally() | ||
403 | kFinalizerRegKey.pushValue(L_); // L_: U finalizer|nil | ||
404 | if (!lua_isnil(L_, -1)) { | ||
405 | lua_pcall(L_, 0, 0, 0); // L_: U | ||
406 | // discard any error that might have occured | ||
407 | lua_settop(L_, 1); | ||
408 | } else { | ||
409 | lua_pop(L_, 1); // L_: U | ||
410 | } | ||
411 | // in case of error, the message is pushed on the stack | ||
412 | STACK_CHECK(L_, 1); | ||
413 | |||
379 | // no need to mutex-protect this as all threads in the universe are gone at that point | 414 | // no need to mutex-protect this as all threads in the universe are gone at that point |
380 | if (_U->timerLinda != nullptr) { // test in case some early internal error prevented Lanes from creating the deep timer | 415 | if (_U->timerLinda != nullptr) { // test in case some early internal error prevented Lanes from creating the deep timer |
381 | [[maybe_unused]] int const _prev_ref_count{ _U->timerLinda->refcount.fetch_sub(1, std::memory_order_relaxed) }; | 416 | [[maybe_unused]] int const _prev_ref_count{ _U->timerLinda->refcount.fetch_sub(1, std::memory_order_relaxed) }; |
@@ -389,6 +424,9 @@ int universe_gc(lua_State* L_) | |||
389 | // remove the protected allocator, if any | 424 | // remove the protected allocator, if any |
390 | _U->protectedAllocator.removeFrom(L_); | 425 | _U->protectedAllocator.removeFrom(L_); |
391 | 426 | ||
427 | // no longer found in the registry | ||
428 | kUniverseFullRegKey.setValue(L_, [](lua_State* L_) { lua_pushnil(L_); }); | ||
429 | kUniverseLightRegKey.setValue(L_, [](lua_State* L_) { lua_pushnil(L_); }); | ||
392 | _U->Universe::~Universe(); | 430 | _U->Universe::~Universe(); |
393 | 431 | ||
394 | return 0; | 432 | return 0; |
diff --git a/src/universe.h b/src/universe.h index f5b31a3..4be6a9a 100644 --- a/src/universe.h +++ b/src/universe.h | |||
@@ -120,6 +120,8 @@ static constexpr RegistryUniqueKey kUniverseLightRegKey{ 0x48BBE9CEAB0BA04Full } | |||
120 | class Universe | 120 | class Universe |
121 | { | 121 | { |
122 | public: | 122 | public: |
123 | static constexpr char const* kFinally{ "finally" }; // update lanes.lua if the name changes! | ||
124 | |||
123 | #ifdef PLATFORM_LINUX | 125 | #ifdef PLATFORM_LINUX |
124 | // Linux needs to check, whether it's been run as root | 126 | // Linux needs to check, whether it's been run as root |
125 | bool const sudo{ geteuid() == 0 }; | 127 | bool const sudo{ geteuid() == 0 }; |
@@ -180,6 +182,7 @@ class Universe | |||
180 | 182 | ||
181 | void closeKeepers(); | 183 | void closeKeepers(); |
182 | void initializeAllocatorFunction(lua_State* L_); | 184 | void initializeAllocatorFunction(lua_State* L_); |
185 | static int InitializeFinalizer(lua_State* L_); | ||
183 | void initializeKeepers(lua_State* L_); | 186 | void initializeKeepers(lua_State* L_); |
184 | void terminateFreeRunningLanes(lua_State* L_, lua_Duration shutdownTimeout_, CancelOp op_); | 187 | void terminateFreeRunningLanes(lua_State* L_, lua_Duration shutdownTimeout_, CancelOp op_); |
185 | }; | 188 | }; |
diff --git a/tests/basic.lua b/tests/basic.lua index cfe6fd5..ab8a080 100644 --- a/tests/basic.lua +++ b/tests/basic.lua | |||
@@ -485,6 +485,6 @@ end | |||
485 | 485 | ||
486 | local nameof_type, nameof_name = lanes.nameof(print) | 486 | local nameof_type, nameof_name = lanes.nameof(print) |
487 | PRINT("name of " .. nameof_type .. " print = '" .. nameof_name .. "'") | 487 | PRINT("name of " .. nameof_type .. " print = '" .. nameof_name .. "'") |
488 | 488 | -- install a finalizer that gets called upon Lanes's internal Universe is GCed. | |
489 | -- | 489 | -- that way, we print our message after anything that can be output by lanes that are still running at that point |
490 | io.stderr:write "Done! :)\n" | 490 | lanes.finally(function() io.stderr:write "\n=======================================\nTEST OK\n" end) |
diff --git a/tests/finalizer.lua b/tests/finalizer.lua index 060e0a6..2acc39d 100644 --- a/tests/finalizer.lua +++ b/tests/finalizer.lua | |||
@@ -10,20 +10,22 @@ | |||
10 | 10 | ||
11 | local lanes = require "lanes" | 11 | local lanes = require "lanes" |
12 | lanes.configure{with_timers=false} | 12 | lanes.configure{with_timers=false} |
13 | local finally = lanes.finally | ||
13 | 14 | ||
14 | local FN= "finalizer-test.tmp" | 15 | local FN = "finalizer-test.tmp" |
15 | 16 | ||
16 | local cleanup | 17 | local cleanup |
17 | 18 | ||
18 | local which= os.time() % 2 -- 0/1 | 19 | local function lane(error_) |
19 | |||
20 | local function lane() | ||
21 | |||
22 | set_finalizer(cleanup) | 20 | set_finalizer(cleanup) |
23 | 21 | ||
24 | local f,err= io.open(FN,"w") | 22 | local st,err = pcall(finally, cleanup) -- should cause an error because called from a lane |
23 | assert(not st, "finally() should have thrown an error") | ||
24 | io.stderr:write("finally() raised error '", err, "'\n") | ||
25 | |||
26 | local f,err = io.open(FN,"w") | ||
25 | if not f then | 27 | if not f then |
26 | error( "Could not create "..FN..": "..err ) | 28 | error( "Could not create "..FN..": "..err) |
27 | end | 29 | end |
28 | 30 | ||
29 | f:write( "Test file that should get removed." ) | 31 | f:write( "Test file that should get removed." ) |
@@ -32,11 +34,10 @@ local function lane() | |||
32 | -- don't forget to close the file immediately, else we won't be able to delete it until f is collected | 34 | -- don't forget to close the file immediately, else we won't be able to delete it until f is collected |
33 | f:close() | 35 | f:close() |
34 | 36 | ||
35 | if which==0 then | 37 | if error_ then |
36 | print "you loose" | 38 | io.stderr:write("Raising ", tostring(error_), "\n") |
37 | error("aa") -- exception here; the value needs NOT be a string | 39 | error(error_, 0) -- exception here; the value needs NOT be a string |
38 | end | 40 | end |
39 | |||
40 | -- no exception | 41 | -- no exception |
41 | end | 42 | end |
42 | 43 | ||
@@ -44,8 +45,8 @@ end | |||
44 | -- This is called at the end of the lane; whether succesful or not. | 45 | -- This is called at the end of the lane; whether succesful or not. |
45 | -- Gets the 'error()' parameter as parameter ('nil' if normal return). | 46 | -- Gets the 'error()' parameter as parameter ('nil' if normal return). |
46 | -- | 47 | -- |
47 | cleanup= function(err) | 48 | cleanup = function(err) |
48 | 49 | io.stderr:write "------------------------ In Worker Finalizer -----------------------\n" | |
49 | -- An error in finalizer will override an error (or success) in the main | 50 | -- An error in finalizer will override an error (or success) in the main |
50 | -- chunk. | 51 | -- chunk. |
51 | -- | 52 | -- |
@@ -57,30 +58,45 @@ cleanup= function(err) | |||
57 | io.stderr:write( "Cleanup after normal return\n" ) | 58 | io.stderr:write( "Cleanup after normal return\n" ) |
58 | end | 59 | end |
59 | 60 | ||
60 | local _,err2= os.remove(FN) | 61 | local _,err2 = os.remove(FN) |
61 | print( "file removal result: ", tostring( err2)) | 62 | io.stderr:write( "file removal result: ", tostring(err2), "\n") |
62 | assert(not err2) -- if this fails, it will be shown in the calling script | 63 | assert(not err2) -- if this fails, it will be shown in the calling script |
63 | -- as an error from the lane itself | 64 | -- as an error from the lane itself |
64 | 65 | ||
65 | io.stderr:write( "Removed file "..FN.."\n" ) | 66 | io.stderr:write( "Removed file "..FN.."\n" ) |
66 | end | 67 | end |
67 | 68 | ||
68 | local lgen = lanes.gen("*", lane) | 69 | -- we need error_trace_level above "minimal" to get a stack trace out of h:join() |
70 | local lgen = lanes.gen("*", {error_trace_level = "basic"}, lane) | ||
71 | |||
72 | local do_test = function(error_) | ||
69 | 73 | ||
70 | io.stderr:write "Launching the lane!\n" | 74 | io.stderr:write "======================== Launching the lane! =======================\n" |
71 | 75 | ||
72 | local h= lgen() | 76 | local h = lgen(error_) |
73 | 77 | ||
74 | local _,err,stack= h:join() -- wait for the lane (no automatic error propagation) | 78 | local _,err,stack = h:join() -- wait for the lane (no automatic error propagation) |
75 | if err then | 79 | if err then |
76 | assert(stack) | 80 | assert(stack, "no stack trace on error, check 'error_trace_level'") |
77 | io.stderr:write( "Lane error: "..tostring(err).."\n" ) | 81 | io.stderr:write( "Lane error: "..tostring(err).."\n" ) |
78 | io.stderr:write( "\t", table.concat(stack,"\t\n"), "\n" ) | 82 | io.stderr:write( "\t", table.concat(stack,"\t\n"), "\n" ) |
83 | end | ||
79 | end | 84 | end |
80 | 85 | ||
81 | local f= io.open(FN,"r") | 86 | do_test(nil) |
82 | if f then | 87 | do_test("An error") |
83 | error( "CLEANUP DID NOT WORK: "..FN.." still exists!" ) | 88 | |
89 | local on_exit = function() | ||
90 | finally(nil) | ||
91 | io.stderr:write "=========================In Lanes Finalizer! =======================\n" | ||
92 | local f = io.open(FN,"r") | ||
93 | if f then | ||
94 | error( "CLEANUP DID NOT WORK: "..FN.." still exists!" ) | ||
95 | else | ||
96 | io.stderr:write(FN .. " was successfully removed\n") | ||
97 | end | ||
98 | io.stderr:write "Finished!\n" | ||
84 | end | 99 | end |
85 | 100 | ||
86 | io.stderr:write "Finished!\n" | 101 | -- this function is called after script exit, when the state is closed |
102 | lanes.finally(on_exit) | ||