From efd14c278c72674fc58cfde2f9023450a755c604 Mon Sep 17 00:00:00 2001
From: Benoit Germain
@@ -62,18 +67,16 @@
-
+
@@ -30,7 +35,7 @@
-
+ Lua Lanes - multithreading in Lua
Lua Lanes - multithreading in Lua
-
- Copyright © 2007-26 Asko Kauppi, Benoit Germain. All rights reserved.
-
- Lua Lanes is published under the same MIT license as Lua 5.1, 5.2, 5.3, 5.4 and 5.5.
-
- This document was revised on 26-Feb-2026, and applies to version 4.0.0. -
- +
+
+ Copyright © 2007-26 Asko Kauppi, Benoit Germain. All rights reserved.
+
+ Lua Lanes is published under the same MIT license as Lua 5.1, 5.2, 5.3, 5.4 and 5.5.
+
+ This document was revised on 3-Mar-2026, and applies to version 4.0.0.
+
- Lanes is included into your software by the regular require "lanes" method. No C side programming is needed; all APIs are Lua side, and most existing extension modules should work seamlessly together with the multiple lanes.
+ Lanes is included into your software by the regular require "lanes" method. No C side programming is needed; all APIs are Lua side, and most existing extension modules should work seamlessly together with the multiple lanes.
Lanes should build and run identically with either Lua 5.1 to Lua 5.5, as well as LuaJIT. @@ -153,11 +156,11 @@
- Lua Lanes is implemented in C++20. It is built simply by make on the supported platforms (make-vc for Visual C++). See README for system specific details and limitations.
+ Lua Lanes is implemented in C++20. It is built simply by make on the supported platforms (make-vc for Visual C++). See README for system specific details and limitations.
- To install Lanes, all you need are the lanes.lua and lanes_core.so|dll files to be reachable by Lua (see LUA_PATH, LUA_CPATH).
+ To install Lanes, all you need are the lanes.lua and lanes_core.so|dll files to be reachable by Lua (see LUA_PATH, LUA_CPATH).
Or use Lua Rocks package management.
require "lanes": to require Lanes
lanes module
lanes.cancel_error: a special error value returned from cancelled laneslanes.collectgarbage(): trigger a GC cycle in all Keeper stateslanes.configure(): configure Laneslanes.coro(): start a coroutine-like lanelanes.finally(): install a function called on Lanes shutdownlanes.gen(): start a lane as a regular functionlanes.genatomic(): obtain an atomic counterlanes.genlock(): obtain an atomic-like data stacklanes.linda(): create a lindalanes.nameof(): find where a value existslanes.null: a light userdata used to represent nil in data transferslanes.thread_priority_range(): obtain the valid range of thread prioritieslanes.now_secs(): obtain the current clock valuelanes.register(): scan modules so that functions using them can be transferredlanes.set_thread_priority(): change thread prioritylanes.set_thread_affinity(): change thread affinitylanes.threads(): obtain a list of all laneslanes.sleep(): sleep for a given durationlanes.timer(): start a timerlanes.timer_lane: the lane that manages timerslanes.timers(): list active timerslane_h
lane_h[]: wait for, and read the values returned by the lanelane_h:cancel(): request the lane to stop runninglane_h.error_trace_level: current setting of error logging levellane_h:get_threadname(): read the thread namelane_h:join(): wait for the lane to close, reading the returned valueslane_h:resume(): resume a coroutine Lanelane_h.status: current status of the lanecancel_test(): check for cancellation requestslane_threadname(): read or change the name of the threadset_finalizer(): install a function called when the lane exitsl
l:cancel(): mark a linda for cancellationl:collectgarbage(): trigger a GC cycle in the linda's Keeper statel:deep(): obtain a light userdata uniquely representing the lindal:dump(): have information about slot contentsl:count(): obtain a count of data items in slotsl:get(): read data without consuming itl:limit(): cap the amount of transiting datal:receive(): read one item of data from multiple slotsl:receive_batched(): read several item of data from a single slotl:restrict(): place a restraint on the operations that can be done on a slotl:send(): append datal:set(): replace the datal:wake(): manually wake blocking callsluaopen_lanes_embedded(): manually initialize Lanes
- luaopen_lanes_embedded leaves the module table on the stack. lanes.configure() must still be called in order to use Lanes.
+ luaopen_lanes_embedded leaves the module table on the stack. lanes.configure() must still be called in order to use Lanes.
- If _luaopen_lanes is nullptr, a default loader will simply attempt the equivalent of luaL_dofile(L, "lanes.lua").
+ If _luaopen_lanes is nullptr, a default loader will simply attempt the equivalent of luaL_dofile(L, "lanes.lua").
@@ -277,33 +280,34 @@
- #include "lanes.hpp"- - int load_lanes_lua(lua_State* L)- {
- // retrieve lanes.lua from wherever it is stored and return the result of its execution- // trivial example 1:- luaL_dofile(L, "lanes.lua");- - // trivial example 2:- luaL_dostring(L, bin2c_lanes_lua);- }- - void embed_lanes(lua_State* L)- {
- // we need base libraries for Lanes for work- luaL_openlibs(L);- ...- // will attempt luaL_dofile(L, "lanes.lua");- luaopen_lanes_embedded(L, nullptr);- lua_pop(L, 1);- // another example with a custom loader- luaopen_lanes_embedded(L, load_lanes_lua);- lua_pop(L, 1);- - // a little test to make sure things work as expected- luaL_dostring(L, "local lanes = require 'lanes'.configure{with_timers = false}; local l = lanes.linda()");
- }+
+ #include "lanes.hpp"
+
+ int load_lanes_lua(lua_State* L)
+ {
+ // retrieve lanes.lua from wherever it is stored and return the result of its execution
+ // trivial example 1:
+ luaL_dofile(L, "lanes.lua");
+
+ // trivial example 2:
+ luaL_dostring(L, bin2c_lanes_lua);
+ }
+
+ void embed_lanes(lua_State* L)
+ {
+ // we need base libraries for Lanes for work
+ luaL_openlibs(L);
+ ...
+ // will attempt luaL_dofile(L, "lanes.lua");
+ luaopen_lanes_embedded(L, nullptr);
+ lua_pop(L, 1);
+ // another example with a custom loader
+ luaopen_lanes_embedded(L, load_lanes_lua);
+ lua_pop(L, 1);
+
+ // a little test to make sure things work as expected
+ luaL_dostring(L, "local lanes = require 'lanes'.configure{with_timers = false}; local l = lanes.linda()");
+ }
|
- Requiring the module follows Lua 5.2+ rules: the module is not available under the global name "lanes", but has to be accessed through require's return value.
- Lanes needs "base", "string" and "table" to be initialized beforehand (plus "jit" for LuaJIT).
+ Requiring the module follows Lua 5.2+ rules: the module is not available under the global name "lanes", but has to be accessed through require's return value.
+ Lanes needs "base", "string" and "table" to be initialized beforehand (plus "jit" for LuaJIT).
- After lanes is required, it is recommended to call lanes.configure(), which is the only function exposed by the module at this point. Calling lanes.configure() will perform one-time initializations and make the rest of the API available.
- If lanes.configure() is not called before starting to use Lanes, it will be called automatically for you with default settings.
- Only the first occurence of require "lanes" must be followed by a call to lanes.configure(). From this point, a simple require "lanes" will be enough wherever you need to require lanes again.
- After being called, lanes.configure() itself is replaced by another function that does nothing with the argument it receives, in case it happens to be called again.
+ After lanes is required, it is recommended to call lanes.configure(), which is the only function exposed by the module at this point. Calling lanes.configure() will perform one-time initializations and make the rest of the API available.
+ If lanes.configure() is not called before starting to use Lanes, it will be called automatically for you with default settings.
+ Only the first occurence of require "lanes" must be followed by a call to lanes.configure(). From this point, a simple require "lanes" will be enough wherever you need to require lanes again.
+ After being called, lanes.configure() itself is replaced by another function that does nothing with the argument it receives, in case it happens to be called again.
- Also, once Lanes is initialized, require() is replaced by another one that wraps it inside a mutex, both in the main state and in all created lanes. This prevents multiple thread-unsafe module initializations from several lanes to occur simultaneously.
+ Also, once Lanes is initialized, require() is replaced by another one that wraps it inside a mutex, both in the main state and in all created lanes. This prevents multiple thread-unsafe module initializations from several lanes to occur simultaneously.
It remains to be seen whether this is actually useful or not: If a module is already threadsafe, protecting its initialization isn't useful. And if it is not, any parallel operation may crash without Lanes being able to do anything about it.
- lanes.configure accepts an optional table as sole argument. -
| name | -value | +name | +values | definition |
- nil/"protected"/function
+ nil+ "protected"+ function |
- If nil, Lua states are created with lua_newstate() and reuse the allocator from the master state. - If "protected", The default allocator obtained from lua_getallocf() in the master state is wrapped inside a critical section and used in all newly created states. - If a function, this function is called prior to creating the state, with a single string argument, either "internal", "keeper" or "lane". It should return a full userdata created as follows: -
lua_newstate(allocF, allocUD).
This option is mostly useful for embedders that want to provide different allocators to each lane, for example to have each one work in a different memory pool thus preventing the need for the allocator itself to be threadsafe.
@@ -394,10 +400,12 @@
.convert_fallback
- nil, lanes.null or 'decay'
+ |
nil+ lanes.null+ 'decay'
- Same as __lanesconvert (see Limitations on data passing), but cannot be a function, because this would have to be transferred to all newly created lanes.
+ Same as |
@@ -418,12 +426,13 @@
__lanesconvert (see Limitations on data passing), but cannot be a function, because this would have to be transferred to all newly created lanes.
.internal_allocator
- "libc"/"allocator"
+ |
"libc"+ "allocator"
Controls which allocator is used for Lanes internal allocations (for Keeper state, linda and lane management).
- If "libc", Lanes uses realloc and free. |
@@ -436,7 +445,7 @@
- If "allocator", Lanes uses whatever was obtained from the "allocator" setting. + If "libc", Lanes uses realloc and free.+ If "allocator", Lanes uses whatever was obtained from the "allocator" setting.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).
If <0, GC runs automatically. This is the default. |
@@ -446,11 +455,11 @@
If 0, GC runs after *every* Keeper operation. - If >0, Keeper states run GC manually with lua_gc(LUA_GCCOLLECT) whenever memory usage reported by lua_gc(LUA_GCCOUNT) reaches this threshold. + If >0, Keeper states run GC manually with lua_gc(LUA_GCCOLLECT) whenever memory usage reported by lua_gc(LUA_GCCOUNT) reaches this threshold.
Check is made after every operation (see below). If memory usage remains above threshold after the GC cycle, an error is raised.
.linda_wake_period
- number > 0
+ number > 0
|
Sets the default period in seconds a linda will wake by itself during blocked operations. Default is never. | - When a linda enters a blocking call (send(), receive(), receive_batched(), sleep()), it normally sleeps either until the operation completes + When a linda enters a blocking call ( send(), receive(), receive_batched(), sleep()), it normally sleeps either until the operation completes
or the specified timeout expires. With this setting, the default behavior can be changed to wake periodically. This can help for example with timing issues where a lane is signalled
for cancellation, but a linda inside the lane was in the middle of processing an operation but did not actually start the wait. This can result in the signal to be ignored, thus
causing the linda to wait out the full operation timeout before cancellation is processed.
@@ -461,10 +470,10 @@
|
- .nb_user_keepers
integer in [0,100] |
+ 0 ≤ number ≤ 100 |
- Controls the number of "user" Keeper state used internally by linda) objects to transfer data between lanes. Default is 0. |
@@ -474,19 +483,20 @@
- Lanes always creates at least one keeper state (of group 0 for the internal timer linda. If nb_user_keepers is 0, the other lindas you create will share this keeper by necessity. + Controls the number of "user" Keeper state used internally by linda) objects to transfer data between lanes. Default is 0.+ Lanes always creates at least one keeper state (of group 0 for the internal timer linda. If nb_user_keepers is 0, the other lindas you create will share this keeper by necessity.If there is more than one Keeper state (in total), linda creation must specify the group it belongs to. .on_state_create
- nil/function
+ |
nil+ function
- If provided, will be called in every created Lua state right after initializing the base libraries, with a single string argument, either "lane" or "keeper". |
@@ -498,10 +508,10 @@
number >= 0
+ If provided, will be called in every created Lua state right after initializing the base libraries, with a single string argument, either "lane" or "keeper".If it is a C function, a C closure will be reconstructed in the created state from the C pointer. Lanes will raise an error if the function has upvalues. If it is a Lua function, it will be transfered normally before the call. Keeper states will call it as well, but only if it is a C function (Keeper states are not able to execute any user Lua code). Typical usage is twofold:
+ That way, all changes in the state can be properly taken into account when building the function lookup database. Default is nil.
- Sets the duration in seconds Lanes will wait for graceful termination of running lanes at application shutdown. Default is 0.25. |
@@ -511,10 +521,10 @@
- Lanes signals all lanes for cancellation with "soft", "hard", and "all" modes, in that order. Each attempt has shutdown_timeout seconds to succeed before the next one. - Then there is a last chance at cleanup with lanes.finally(). If some lanes are still running after that point, shutdown will either freeze or throw. It is YOUR responsibility to cleanup properly after yourself. - IMPORTANT: If there are still running lanes at shutdown, an error is raised, which will be propagated by Lua to the handler installed by lua_setwarnf. If the finalizer returned a value, this will be used as the error message. + Sets the duration in seconds Lanes will wait for graceful termination of running lanes at application shutdown. Default is 0.25.+ Lanes signals all lanes for cancellation with "soft", "hard", and "all" modes, in that order. Each attempt has shutdown_timeout seconds to succeed before the next one.+ Then there is a last chance at cleanup with lanes.finally(). If some lanes are still running after that point, shutdown will either freeze or throw. It is YOUR responsibility to cleanup properly after yourself.
+ IMPORTANT: If there are still running lanes at shutdown, an error is raised, which will be propagated by Lua to the handler installed by lua_setwarnf. If the finalizer returned a value, this will be used as the error message.LANES SHUTDOWN WILL NOT BE COMPLETE IN THAT CASE, AND THE SUBSEQUENT CONSEQUENCES ARE UNDEFINED! .strip_functions
- nil/boolean
+ |
nil/boolean
- Controls function bytecode stripping when dumping them for lane transfer. Choose between faster copies or more debug info. Default is true.
+ Controls function bytecode stripping when dumping them for lane transfer. Choose between faster copies or more debug info. Default is |
true.
.track_lanes
- nil/boolean
+ |
nil/boolean
- Any non-nil|false value instructs Lanes keeps track of all lanes, so that lanes.threads() can list them. If false, lanes.threads() will raise an error when called.
- Default is false.
+ Any non- |
nil|false value instructs Lanes keeps track of all lanes, so that lanes.threads() can list them. If false, lanes.threads() will raise an error when called.
+ Default is false.
.verbose_errors
- nil/boolean
+ |
nil/boolean
- If equal to true, Lanes will collect more information when transfering stuff across Lua states to help identify errors (with a cost).
- Default is false.
+ If equal to |
@@ -548,11 +558,11 @@
true, Lanes will collect more information when transfering stuff across Lua states to help identify errors (with a cost).
+ Default is false.
.with_timers
- nil/boolean
+ |
nil/boolean
- If equal to false or nil, Lanes doesn't start the timer service, and the associated API will be absent from the interface (see below).
- Default is false.
+ If equal to |
false or nil, Lanes doesn't start the timer service, and the associated API will be absent from the interface (see below).
+ Default is false.
Once Lanes is configured, one should register with Lanes the modules exporting functions/userdata that will be transferred either during lane generation or through lindas.
- A full GC cycle can be triggered on all keeper states with lanes.collectgarbage(). This can force the collection of stale storage data for a collected Linda.
+ A full GC cycle can be triggered on all keeper states with @@ -606,11 +618,11 @@
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). @@ -623,13 +635,15 @@
- The function returned by lanes.gen() is a "generator" for launching any number of lanes. They will share code, options, initial globals, but the particular arguments may vary. Only calling the generator function actually launches a lane, and provides a handle for controlling it.
+ The function returned by
- Lanes automatically copies upvalues over to the new lanes, so you need not wrap all the required elements into one 'wrapper' function. If lane_func uses some local values, or local functions, they will be there also in the new lanes.
+ Lanes automatically copies upvalues over to the new lanes, so you need not wrap all the required elements into one 'wrapper' function. If
- Any non-nil value causes initialization of "base" and "jit" (the latter only for LuaJIT-based builds). @@ -830,11 +835,11 @@ -
- Each lane gets a global function lane_threadname() that it can use anytime to do both read and change the thread name. Supported debuggers are Microsoft Visual Studio (for the C side) and Decoda (for the Lua side).
- If a lane body pulls a C function or userdata exposed by a module required before Lanes itself (thus not through a hooked require()), the lane generator creation will raise an error.
- The name in the message is a path where it was found by scanning _G and the registry. As a utility, the name guessing functionality is exposed as such:
+ If a lane body pulls a C function or userdata exposed by a module required before Lanes itself (thus not through a hooked - A yielded coroutine lane has a "suspended" status. It can be resumed with lane_h:resume(values...), which returns the yielded values. - The latter can also be the returned values of lane_h:join() or accessed by regular lane indexing (see Results and errors). + Coroutine lanes operate mostly like regular coroutines. They can use coroutine.yield() normally.+ A yielded coroutine lane has a "suspended" status. It can be resumed with lane_h:resume(values...), which returns the yielded values.
+ The latter can also be the returned values of lane_h:join() or accessed by regular lane indexing (see Results and errors).
- If a coroutine lane is suspended when it is joined either by indexing or lane_h:join(), active to-be-closed variables are closed at that point, and the Lane can no longer be resumed. + Just like regular coroutines, the reply values passed to h:resume() are returned to the lane body at the coroutine.yield() point.+ If a coroutine lane is suspended when it is joined either by indexing or lane_h:join(), active to-be-closed variables are closed at that point, and the Lane can no longer be resumed.
Free running lanes@@ -994,9 +999,9 @@
Besides setting a default priority in the generator settings, each thread can change its own priority at will. This is also true for the main Lua state.
- Read back the value set during lane generation, one of "basic", "minimal", "extended".
+ Read back the value set during lane generation, one of
- The current execution state of a lane can be read via its status member, providing one of these values:
+ The current execution state of a lane can be read via its
- Only available if lane tracking is enabled by setting track_lanes.
+ Only available if lane tracking is enabled by setting
- Makes sure lane has finished or yielded, and gives its first (maybe only) return value. Other return values will be available in other lane_h indices.
+ Makes sure lane has finished or yielded, and gives its first (maybe only) return value. Other return values will be available in other
- Waits until the lane finishes/yields, or timeout seconds have passed (forever if nil).
lanes.gen() is to-be-closed, closing the value will cause a call to join().
- cancel() sends a cancellation request to the lane.
+
- Returns true, lane_h.status if lane was already done (in "done", "error" or "cancelled" status), or the cancellation was fruitful within timeout_secs timeout period. mode. It can be one of:
- If mode is not specified, it defaults to "hard".
+ If
- If wake_lane is true, the lane is also signalled so that execution returns from any pending linda operation. linda operations detecting the cancellation request return lanes.cancel_error.
+ If
- timeout is an optional number >= 0. Defaults to infinite if left unspecified or nil.
+ If the lane is still running after the timeout expired, there is a chance Lanes will freeze forever at shutdown when failing to terminate all free-running lanes within the specified timeout.
- Cancellation is tested before going to sleep in receive(), receive_batched() or send() calls and after executing cancelstep Lua statements. A pending receive()or send() call is awakened.
+ Cancellation is tested before going to sleep in |
|---|
- Lanes installs the function cancel_test() in each created lane to manually test for cancel requests.
- It returns false when no cancel is pending, "soft" on a soft cancel request, and raises
- lanes.cancel_error on a hard cancel request. Passing true as the optional argument suppresses
- the raise and returns "hard" instead, which is useful when the lane needs to distinguish the cancel
+ Lanes installs the function cancel_test() in each created lane to manually test for cancel requests.
+ It returns false when no cancel is pending, "soft" on a soft cancel request, and raises
+ lanes.cancel_error on a hard cancel request. Passing true as the optional argument suppresses
+ the raise and returns "hard" instead, which is useful when the lane needs to distinguish the cancel
mode before deciding how to react.
- The regular Lua error function is usable in lanes for throwing exceptions. What Lua does not offer, however, is scoped finalizers
- that would get called when a certain block of instructions gets exited, whether through peaceful return or abrupt error.
+ The regular Lua error function is usable in lanes for throwing exceptions. What Lua does not offer, however, is scoped finalizers
+ that would get called when a certain block of instructions gets exited, whether through peaceful return or abrupt error.
- Lanes registers a function set_finalizer() in the lane's Lua state for doing this.
+ Lanes registers a function set_finalizer() in the lane's Lua state for doing this.
Any functions given to it will be called in the lane's Lua state, just prior to closing it. It is possible to set more than one finalizer. They are called in LIFO order.
on_state_create, which is handled in a special way.
:receive() (not in).:get() (not rd).:send() and :set() (not out).send() allows for sending multiple values -atomically- to a given slot.receive() can wait for multiple slots at once.receive_batched() can be used to consume more than one value from a single slot, as in linda:receive_batched(1.0, "slot", 3, 6).restrict() can restrain a particular slot to function either with send()/receive() or set()/get().:send() wait).tostring(linda) returns a string of the form "Linda: <opt_name>"[0,nb_user_keepers]).
+ Lanes has an internal linda used for timers and lanes.wait(); this linda uses group 0.
lua_gc STOP/RESTART pair.
This is to prevent potential collection of a linda during another linda's operation, as this can cause a crash if they are bound to the same Keeper state.
- Argument to lanes.linda() is either nil or a single table. The table may contain the following entries:
+ Argument to lanes.linda() is either nil or a single table. The table may contain the following entries:
close_handler: a callable object (function or table/userdata with __call metamethod). If provided, and the linda is to-be-closed (Lua 5.4+), it will be called with all the provided arguments. For older Lua versions, its presence is ignored.group: an integer between 0 and the number of Keeper states. Mandatory if Lanes is configured with more than one Keeper state. Group 0 is used by the internal timer linda.name: a string. Converting the linda to a string will yield the provided name prefixed by "Linda: ".
+ If omitted or empty, it will evaluate to the string representation of a hexadecimal number uniquely representing that linda when the linda is converted to a string. The numeric value is the same as returned by linda:deep()."auto", Lanes will try to construct a name from the source location that called lanes.linda(). If that fails, the linda name will be "<unresolved>".
wake_period: a number > 0 (unit: seconds). If provided, overrides linda_wake_period provided to lanes.configure().
- By default, queue sizes are unlimited but limits can be enforced using the limit() method. This can be useful to balance execution speeds in a producer/consumer scenario.
- A limit of 0 is allowed to block everything. "unlimited" removes the limit.
- If the slot was full but the limit change added some room, limit() first return value is true and the linda is signalled so that send()-blocked threads are awakened, else the return value is false.
- If no limit is provided, limit() first return value is the current limit for the specified slot.
- The second returned value is a string representing the fill status relatively to the slot's current limit (one of "over", "under", "exact").
- Whether reading or writing, if the linda is cancelled, limit() returns nil, lanes.cancel_error.
+ By default, queue sizes are unlimited but limits can be enforced using the limit() method. This can be useful to balance execution speeds in a producer/consumer scenario.
+ A limit of 0 is allowed to block everything. "unlimited" removes the limit.
+ If the slot was full but the limit change added some room, limit() first return value is true and the linda is signalled so that send()-blocked threads are awakened, else the return value is false.
+ If no limit is provided, limit() first return value is the current limit for the specified slot.
+ The second returned value is a string representing the fill status relatively to the slot's current limit (one of "over", "under", "exact").
+ Whether reading or writing, if the linda is cancelled, limit() returns nil, lanes.cancel_error.
@@ -1476,12 +1467,12 @@ |
- It is possible to restrict a particular slot in a Linda to either send()/receive() or set()/get() operations.
- Possible modes are "none", "set/get" or "send/receive".
- If a new mode is specified, restrict() updates the mode and returns the previous one.
- If no mode is specified, restrict() does nothing and returns the current mode.
- If the linda is cancelled, restrict() returns nil, lanes.cancel_error.
- If an unknown mode is specified, restrict() raises an error.
+ It is possible to restrict a particular slot in a Linda to either send()/receive() or set()/get() operations.
+ Possible modes are "none", "set/get" or "send/receive".
+ If a new mode is specified, restrict() updates the mode and returns the previous one.
+ If no mode is specified, restrict() does nothing and returns the current mode.
+ If the linda is cancelled, restrict() returns nil, lanes.cancel_error.
+ If an unknown mode is specified, restrict() raises an error.
@@ -1489,7 +1480,7 @@ |
- Timeouts are given in seconds (>= 0, millisecond accuracy) or nil. Timeout can be omitted only if the first slot is not a number (then it is equivalent to an infinite duration).
+ Timeouts are given in seconds (>= 0, millisecond accuracy) or nil. Timeout can be omitted only if the first slot is not a number (then it is equivalent to an infinite duration).
Each slot acts as a FIFO queue. There is no limit to the number of slots a linda may contain. Different lindas can have identical slots, which are totally unrelated.
- If linda.null or lanes.null is sent as data in a linda, it will be read as a nil.
+ If linda.null or lanes.null is sent as data in a linda, it will be read as a nil.
- send() raises an error if no data is provided after the slot.
- send() raises an error if called when a restriction forbids its use on the provided slot.
- send() raises lanes.cancel_error if interrupted by a hard cancel request.
- send() return values can be:
+ send() raises an error if no data is provided after the slot.
+ send() raises an error if called when a restriction forbids its use on the provided slot.
+ send() raises lanes.cancel_error if interrupted by a hard cancel request.
+ send() return values can be:
true on success.nil, "timeout" if the queue limit was met, and the queue did not empty enough during the given duration.nil, lanes.cancel_error if interrupted by a soft cancel request.
- receive() and receive_batched() raise an error if called when a restriction forbids their use on any provided slot.
- receive_batched() will raise an error if min_count < 1 or max_count < min_count.
+ receive() and receive_batched() raise an error if called when a restriction forbids their use on any provided slot.
+ receive_batched() will raise an error if min_count < 1 or max_count < min_count.
- Unbatched receive() return values can be:
+ Unbatched receive() return values can be:
nil, lanes.cancel_error if interrupted by a soft cancel request.nil, "timeout" if nothing was available.nil can be sent and received; the first returned value slot will tell it apart from a timeout.
- set() and get() raise an error if used when a restriction forbids their use on the provided slot.
- Unless a particular slot is constrained with restrict(), get()/set() and send()/receive() can be used together; reading a slot essentially peeks the next outcoming value of a queue.
- get()/set() are for accessing a slot without queuing or consuming. They can be used for making shared tables of storage among the lanes.
- set() never blocks because it ignores the limit. It overwrites existing values and clears any possible queued entries.
+ set() and get() raise an error if used when a restriction forbids their use on the provided slot.
+ Unless a particular slot is constrained with restrict(), get()/set() and send()/receive() can be used together; reading a slot essentially peeks the next outcoming value of a queue.
+ get()/set() are for accessing a slot without queuing or consuming. They can be used for making shared tables of storage among the lanes.
+ set() never blocks because it ignores the limit. It overwrites existing values and clears any possible queued entries.
- get() can read several values at once, and does not block. Return values ares:
+ get() can read several values at once, and does not block. Return values ares:
nil, lanes.cancel_error in case of cancellation.number, val... where number is the actual count of items obtained from the linda (can be 0).
- set() can write several values at the specified slot. Writing nil values is possible, and clearing the contents at the specified slot is done by not providing any value.
- If set() actually stores data, the linda is signalled for write, so that receive()-blocked Lanes are awakened.
+ set() can write several values at the specified slot. Writing nil values is possible, and clearing the contents at the specified slot is done by not providing any value.
+ If set() actually stores data, the linda is signalled for write, so that receive()-blocked Lanes are awakened.
Clearing the contents of a non-existent slot does not create it!
- If the slot was full but the new data count of the slot after set() is below its limit, set() first return value is true and the linda is also signaled for read, so that send()-blocked Lanes are awakened.
- If the slot was not already full, nothing additional happens, and set() first return value is false.
- The second return value is a string representing the fill status relatively to the slot's current limit (one of "over", "under", "exact").
+ If the slot was full but the new data count of the slot after set() is below its limit, set() first return value is true and the linda is also signaled for read, so that send()-blocked Lanes are awakened.
+ If the slot was not already full, nothing additional happens, and set() first return value is false.
+ The second return value is a string representing the fill status relatively to the slot's current limit (one of "over", "under", "exact").
- Trying to send or receive data through a cancelled linda does nothing and returns lanes.cancel_error.
+ Trying to send or receive data through a cancelled linda does nothing and returns lanes.cancel_error.
@@ -1583,7 +1574,7 @@ |
- Forces a full garbage collect cycle inside the Keeper state assigned to the linda (same as lua_gc(L, LUA_GCCOLLECT)).
+ Forces a full garbage collect cycle inside the Keeper state assigned to the linda (same as lua_gc(L, LUA_GCCOLLECT)).
All lindas sharing the same Keeper state will have to wait for the operation to complete before being able to resume regular service.
Can be useful to clean stale storage after some keys are cleaned.
- Returns a table describing the full contents of a linda, or nil if the linda wasn't used yet.
- If Decoda support is enabled with HAVE_DECODA_SUPPORT(), the linda metatable contains a __towatch special function that generates a similar table used for debug display.
+ Returns a table describing the full contents of a linda, or nil if the linda wasn't used yet.
+ If Decoda support is enabled with HAVE_DECODA_SUPPORT(), the linda metatable contains a __towatch special function that generates a similar table used for debug display.
@@ -1626,10 +1617,10 @@ |
- linda:cancel() signals the linda so that lanes waiting for read, write, or both, wake up.
- All linda operations (including get() and set()) will return lanes.cancel_error as when the calling lane is soft-cancelled as long as the linda is marked as cancelled.
- "none" reset the linda's cancel status, but doesn't signal it.
- linda.status reads the current cancel status.
+ linda:cancel() signals the linda so that lanes waiting for read, write, or both, wake up.
+ All linda operations (including get() and set()) will return lanes.cancel_error as when the calling lane is soft-cancelled as long as the linda is marked as cancelled.
+ "none" reset the linda's cancel status, but doesn't signal it.
+ linda.status reads the current cancel status.
If not void, the lane's cancel status overrides the linda's cancel status.
lanes.cancel_error.@@ -1679,7 +1670,7 @@
lanes.configure() the less this should be a problem.
- Timers are implemented as a lane. They can be enabled by setting "with_timers" to true in lanes.configure() settings.
+ Timers are implemented as a lane. They can be enabled by setting "with_timers" to true in lanes.configure() settings.
- Timers can be run once, or in a reoccurring fashion (period_secs > 0). The first occurrence can be given either as a date or as a relative delay in seconds. The date table is like what os.date("*t") returns, in the local time zone.
+ Timers can be run once, or in a reoccurring fashion (period_secs > 0). The first occurrence can be given either as a date or as a relative delay in seconds. The date table is like what os.date("*t") returns, in the local time zone.
- Once a timer expires, the slot is set with the current time (in seconds, same offset as os.time() but with millisecond accuracy). The slot can be waited upon using the regular linda:receive() method.
+ Once a timer expires, the slot is set with the current time (in seconds, same offset as os.time() but with millisecond accuracy). The slot can be waited upon using the regular linda:receive() method.
- A timer can be stopped simply with first_secs=0|nil and no period.
+ A timer can be stopped simply with first_secs=0|nil and no period.
@@ -1738,25 +1729,25 @@ end |
- NOTE: Timer slots are set, not queued, so missing a beat is possible especially if the timer cycle is extremely small. The slot value can be used to know the actual time passed. -
+| NOTE: | +Timer slots are set, not queued, so missing a beat is possible especially if the timer cycle is extremely small. The slot value can be used to know the actual time passed. | +|
| Design note: |
-
- Having the API as lanes.timer() is intentional. Another alternative would be linda_h:timer() but timers are not traditionally seen to be part of lindas. Also, it would mean any lane getting a linda handle would be able to modify timers on it.
- A third choice could be abstracting the timers out of linda realm altogether (timer_h = lanes.timer(date|first_secs, period_secs )) but that would mean separate waiting functions for timers, and lindas.
+ Having the API as lanes.timer() is intentional. Another alternative would be linda_h:timer() but timers are not traditionally seen to be part of lindas. Also, it would mean any lane getting a linda handle would be able to modify timers on it.
+ A third choice could be abstracting the timers out of linda realm altogether (timer_h = lanes.timer(date|first_secs, period_secs )) but that would mean separate waiting functions for timers, and lindas.
Even if a linda object and slot was returned, that slot couldn't be waited upon simultaneously with one's general linda events.
The current system gives maximum capabilities with minimum API, and any smoothenings can easily be crafted in Lua at the application level.
-
|
{[{linda, slot, {when [, period]}}[,...]]} = lanes.timers()
@@ -1764,7 +1755,7 @@
|