diff options
| author | Benoit Germain <bnt.germain@gmail.com> | 2015-12-29 10:09:34 +0100 |
|---|---|---|
| committer | Benoit Germain <bnt.germain@gmail.com> | 2015-12-29 10:09:34 +0100 |
| commit | f1b69462a9a7ebf5d761bb8fc3b043b8898969a7 (patch) | |
| tree | 4ab132aad136f95bc23c097af9f5b3925e71644f /src | |
| parent | 295ba159356050037419cbdb5ba1d1e3362ce391 (diff) | |
| parent | 4f52790f4896ef6b05f3784bdb668ac3f8144aac (diff) | |
| download | lanes-f1b69462a9a7ebf5d761bb8fc3b043b8898969a7.tar.gz lanes-f1b69462a9a7ebf5d761bb8fc3b043b8898969a7.tar.bz2 lanes-f1b69462a9a7ebf5d761bb8fc3b043b8898969a7.zip | |
Merge pull request #121 from aryajur/master
Improved the lanes.lua file
Diffstat (limited to 'src')
| -rw-r--r-- | src/lanes.lua | 967 |
1 files changed, 489 insertions, 478 deletions
diff --git a/src/lanes.lua b/src/lanes.lua index 5df406a..6cbbd65 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
| @@ -41,6 +41,8 @@ local core = require "lanes.core" | |||
| 41 | -- almost everything module() does is done by require() anyway | 41 | -- almost everything module() does is done by require() anyway |
| 42 | -- -> simply create a table, populate it, return it, and be done | 42 | -- -> simply create a table, populate it, return it, and be done |
| 43 | local lanes = {} | 43 | local lanes = {} |
| 44 | local lanesMeta = {} | ||
| 45 | setmetatable(lanes,lanesMeta) | ||
| 44 | 46 | ||
| 45 | -- this function is available in the public interface until it is called, after which it disappears | 47 | -- this function is available in the public interface until it is called, after which it disappears |
| 46 | lanes.configure = function( settings_) | 48 | lanes.configure = function( settings_) |
| @@ -52,7 +54,8 @@ lanes.configure = function( settings_) | |||
| 52 | if not string then | 54 | if not string then |
| 53 | error( "To use 'lanes', you will also need to have 'string' available.", 2) | 55 | error( "To use 'lanes', you will also need to have 'string' available.", 2) |
| 54 | end | 56 | end |
| 55 | 57 | -- Configure called so remove metatable from lanes | |
| 58 | setmetatable(lanes,nil) | ||
| 56 | -- | 59 | -- |
| 57 | -- Cache globals for code that might run under sandboxing | 60 | -- Cache globals for code that might run under sandboxing |
| 58 | -- | 61 | -- |
| @@ -131,548 +134,548 @@ lanes.configure = function( settings_) | |||
| 131 | local core_lane_new = assert( core.lane_new) | 134 | local core_lane_new = assert( core.lane_new) |
| 132 | local max_prio = assert( core.max_prio) | 135 | local max_prio = assert( core.max_prio) |
| 133 | 136 | ||
| 134 | lanes.ABOUT = | 137 | lanes.ABOUT = |
| 135 | { | 138 | { |
| 136 | author= "Asko Kauppi <akauppi@gmail.com>, Benoit Germain <bnt.germain@gmail.com>", | 139 | author= "Asko Kauppi <akauppi@gmail.com>, Benoit Germain <bnt.germain@gmail.com>", |
| 137 | description= "Running multiple Lua states in parallel", | 140 | description= "Running multiple Lua states in parallel", |
| 138 | license= "MIT/X11", | 141 | license= "MIT/X11", |
| 139 | copyright= "Copyright (c) 2007-10, Asko Kauppi; (c) 2011-13, Benoit Germain", | 142 | copyright= "Copyright (c) 2007-10, Asko Kauppi; (c) 2011-13, Benoit Germain", |
| 140 | version = assert( core.version) | 143 | version = assert( core.version) |
| 141 | } | 144 | } |
| 142 | 145 | ||
| 143 | 146 | ||
| 144 | -- Making copies of necessary system libs will pass them on as upvalues; | 147 | -- Making copies of necessary system libs will pass them on as upvalues; |
| 145 | -- only the first state doing "require 'lanes'" will need to have 'string' | 148 | -- only the first state doing "require 'lanes'" will need to have 'string' |
| 146 | -- and 'table' visible. | 149 | -- and 'table' visible. |
| 147 | -- | 150 | -- |
| 148 | local function WR(str) | 151 | local function WR(str) |
| 149 | io.stderr:write( str.."\n" ) | 152 | io.stderr:write( str.."\n" ) |
| 150 | end | 153 | end |
| 151 | 154 | ||
| 152 | local function DUMP( tbl ) | 155 | local function DUMP( tbl ) |
| 153 | if not tbl then return end | 156 | if not tbl then return end |
| 154 | local str="" | 157 | local str="" |
| 155 | for k,v in pairs(tbl) do | 158 | for k,v in pairs(tbl) do |
| 156 | str= str..k.."="..tostring(v).."\n" | 159 | str= str..k.."="..tostring(v).."\n" |
| 157 | end | 160 | end |
| 158 | WR(str) | 161 | WR(str) |
| 159 | end | 162 | end |
| 160 | 163 | ||
| 161 | 164 | ||
| 162 | ---=== Laning ===--- | 165 | ---=== Laning ===--- |
| 163 | 166 | ||
| 164 | -- lane_h[1..n]: lane results, same as via 'lane_h:join()' | 167 | -- lane_h[1..n]: lane results, same as via 'lane_h:join()' |
| 165 | -- lane_h[0]: can be read to make sure a thread has finished (always gives 'true') | 168 | -- lane_h[0]: can be read to make sure a thread has finished (always gives 'true') |
| 166 | -- lane_h[-1]: error message, without propagating the error | 169 | -- lane_h[-1]: error message, without propagating the error |
| 167 | -- | 170 | -- |
| 168 | -- Reading a Lane result (or [0]) propagates a possible error in the lane | 171 | -- Reading a Lane result (or [0]) propagates a possible error in the lane |
| 169 | -- (and execution does not return). Cancelled lanes give 'nil' values. | 172 | -- (and execution does not return). Cancelled lanes give 'nil' values. |
| 170 | -- | 173 | -- |
| 171 | -- lane_h.state: "pending"/"running"/"waiting"/"done"/"error"/"cancelled" | 174 | -- lane_h.state: "pending"/"running"/"waiting"/"done"/"error"/"cancelled" |
| 172 | -- | 175 | -- |
| 173 | -- Note: Would be great to be able to have '__ipairs' metamethod, that gets | 176 | -- Note: Would be great to be able to have '__ipairs' metamethod, that gets |
| 174 | -- called by 'ipairs()' function to custom iterate objects. We'd use it | 177 | -- called by 'ipairs()' function to custom iterate objects. We'd use it |
| 175 | -- for making sure a lane has ended (results are available); not requiring | 178 | -- for making sure a lane has ended (results are available); not requiring |
| 176 | -- the user to precede a loop by explicit 'h[0]' or 'h:join()'. | 179 | -- the user to precede a loop by explicit 'h[0]' or 'h:join()'. |
| 177 | -- | 180 | -- |
| 178 | -- Or, even better, 'ipairs()' should start valuing '__index' instead | 181 | -- Or, even better, 'ipairs()' should start valuing '__index' instead |
| 179 | -- of using raw reads that bypass it. | 182 | -- of using raw reads that bypass it. |
| 180 | -- | 183 | -- |
| 181 | ----- | 184 | ----- |
| 182 | -- lanes.gen( [libs_str|opt_tbl [, ...],] lane_func ) ( [...] ) -> h | 185 | -- lanes.gen( [libs_str|opt_tbl [, ...],] lane_func ) ( [...] ) -> h |
| 183 | -- | 186 | -- |
| 184 | -- 'libs': nil: no libraries available (default) | 187 | -- 'libs': nil: no libraries available (default) |
| 185 | -- "": only base library ('assert', 'print', 'unpack' etc.) | 188 | -- "": only base library ('assert', 'print', 'unpack' etc.) |
| 186 | -- "math,os": math + os + base libraries (named ones + base) | 189 | -- "math,os": math + os + base libraries (named ones + base) |
| 187 | -- "*": all standard libraries available | 190 | -- "*": all standard libraries available |
| 188 | -- | 191 | -- |
| 189 | -- 'opt': .priority: int (-3..+3) smaller is lower priority (0 = default) | 192 | -- 'opt': .priority: int (-3..+3) smaller is lower priority (0 = default) |
| 190 | -- | 193 | -- |
| 191 | -- .cancelstep: bool | uint | 194 | -- .cancelstep: bool | uint |
| 192 | -- false: cancellation check only at pending Linda operations | 195 | -- false: cancellation check only at pending Linda operations |
| 193 | -- (send/receive) so no runtime performance penalty (default) | 196 | -- (send/receive) so no runtime performance penalty (default) |
| 194 | -- true: adequate cancellation check (same as 100) | 197 | -- true: adequate cancellation check (same as 100) |
| 195 | -- >0: cancellation check every x Lua lines (small number= faster | 198 | -- >0: cancellation check every x Lua lines (small number= faster |
| 196 | -- reaction but more performance overhead) | 199 | -- reaction but more performance overhead) |
| 197 | -- | 200 | -- |
| 198 | -- .globals: table of globals to set for a new thread (passed by value) | 201 | -- .globals: table of globals to set for a new thread (passed by value) |
| 199 | -- | 202 | -- |
| 200 | -- .required: table of packages to require | 203 | -- .required: table of packages to require |
| 201 | -- | 204 | -- |
| 202 | -- .gc_cb: function called when the lane handle is collected | 205 | -- .gc_cb: function called when the lane handle is collected |
| 203 | -- | 206 | -- |
| 204 | -- ... (more options may be introduced later) ... | 207 | -- ... (more options may be introduced later) ... |
| 205 | -- | 208 | -- |
| 206 | -- Calling with a function parameter ('lane_func') ends the string/table | 209 | -- Calling with a function parameter ('lane_func') ends the string/table |
| 207 | -- modifiers, and prepares a lane generator. | 210 | -- modifiers, and prepares a lane generator. |
| 208 | |||
| 209 | local valid_libs = | ||
| 210 | { | ||
| 211 | ["package"] = true, | ||
| 212 | ["table"] = true, | ||
| 213 | ["io"] = true, | ||
| 214 | ["os"] = true, | ||
| 215 | ["string"] = true, | ||
| 216 | ["math"] = true, | ||
| 217 | ["debug"] = true, | ||
| 218 | ["bit32"] = true, -- Lua 5.2 only, ignored silently under 5.1 | ||
| 219 | ["utf8"] = true, -- Lua 5.3 only, ignored silently under 5.1 and 5.2 | ||
| 220 | -- | ||
| 221 | ["base"] = true, | ||
| 222 | ["coroutine"] = true, -- part of "base" in Lua 5.1 | ||
| 223 | ["lanes.core"] = true | ||
| 224 | } | ||
| 225 | |||
| 226 | local raise_option_error = function( name_, tv_, v_) | ||
| 227 | error( "Bad '" .. name_ .. "' option: " .. tv_ .. " " .. string_format( "%q", tostring( v_)), 4) | ||
| 228 | end | ||
| 229 | 211 | ||
| 230 | local opt_validators = | 212 | local valid_libs = |
| 231 | { | 213 | { |
| 232 | priority = function( v_) | 214 | ["package"] = true, |
| 233 | local tv = type( v_) | 215 | ["table"] = true, |
| 234 | return (tv == "number") and v_ or raise_option_error( "priority", tv, v_) | 216 | ["io"] = true, |
| 235 | end, | 217 | ["os"] = true, |
| 236 | cancelstep = function( v_) | 218 | ["string"] = true, |
| 237 | local tv = type( v_) | 219 | ["math"] = true, |
| 238 | return (tv == "number") and v_ or (v_ == true) and 100 or (v_ == false) and 0 or raise_option_error( "cancelstep", tv, v_) | 220 | ["debug"] = true, |
| 239 | end, | 221 | ["bit32"] = true, -- Lua 5.2 only, ignored silently under 5.1 |
| 240 | globals = function( v_) | 222 | ["utf8"] = true, -- Lua 5.3 only, ignored silently under 5.1 and 5.2 |
| 241 | local tv = type( v_) | 223 | -- |
| 242 | return (tv == "table") and v_ or raise_option_error( "globals", tv, v_) | 224 | ["base"] = true, |
| 243 | end, | 225 | ["coroutine"] = true, -- part of "base" in Lua 5.1 |
| 244 | package = function( v_) | 226 | ["lanes.core"] = true |
| 245 | local tv = type( v_) | 227 | } |
| 246 | return (tv == "table") and v_ or raise_option_error( "package", tv, v_) | 228 | |
| 247 | end, | 229 | local raise_option_error = function( name_, tv_, v_) |
| 248 | required = function( v_) | 230 | error( "Bad '" .. name_ .. "' option: " .. tv_ .. " " .. string_format( "%q", tostring( v_)), 4) |
| 249 | local tv = type( v_) | ||
| 250 | return (tv == "table") and v_ or raise_option_error( "required", tv, v_) | ||
| 251 | end, | ||
| 252 | gc_cb = function( v_) | ||
| 253 | local tv = type( v_) | ||
| 254 | return (tv == "function") and v_ or raise_option_error( "gc_cb", tv, v_) | ||
| 255 | end | ||
| 256 | } | ||
| 257 | |||
| 258 | -- PUBLIC LANES API | ||
| 259 | -- receives a sequence of strings and tables, plus a function | ||
| 260 | local gen = function( ...) | ||
| 261 | -- aggregrate all strings together, separated by "," as well as tables | ||
| 262 | -- the strings are a list of libraries to open | ||
| 263 | -- the tables contain the lane options | ||
| 264 | local opt = {} | ||
| 265 | local libs = nil | ||
| 266 | |||
| 267 | local n = select( '#', ...) | ||
| 268 | |||
| 269 | -- we need at least a function | ||
| 270 | if n == 0 then | ||
| 271 | error( "No parameters!", 2) | ||
| 272 | end | 231 | end |
| 273 | 232 | ||
| 274 | -- all arguments but the last must be nil, strings, or tables | 233 | local opt_validators = |
| 275 | for i = 1, n - 1 do | 234 | { |
| 276 | local v = select( i, ...) | 235 | priority = function( v_) |
| 277 | local tv = type( v) | 236 | local tv = type( v_) |
| 278 | if tv == "string" then | 237 | return (tv == "number") and v_ or raise_option_error( "priority", tv, v_) |
| 279 | libs = libs and libs .. "," .. v or v | 238 | end, |
| 280 | elseif tv == "table" then | 239 | cancelstep = function( v_) |
| 281 | for k, vv in pairs( v) do | 240 | local tv = type( v_) |
| 282 | opt[k]= vv | 241 | return (tv == "number") and v_ or (v_ == true) and 100 or (v_ == false) and 0 or raise_option_error( "cancelstep", tv, v_) |
| 283 | end | 242 | end, |
| 284 | elseif v == nil then | 243 | globals = function( v_) |
| 285 | -- skip | 244 | local tv = type( v_) |
| 286 | else | 245 | return (tv == "table") and v_ or raise_option_error( "globals", tv, v_) |
| 287 | error( "Bad parameter " .. i .. ": " .. tv .. " " .. string_format( "%q", tostring( v)), 2) | 246 | end, |
| 247 | package = function( v_) | ||
| 248 | local tv = type( v_) | ||
| 249 | return (tv == "table") and v_ or raise_option_error( "package", tv, v_) | ||
| 250 | end, | ||
| 251 | required = function( v_) | ||
| 252 | local tv = type( v_) | ||
| 253 | return (tv == "table") and v_ or raise_option_error( "required", tv, v_) | ||
| 254 | end, | ||
| 255 | gc_cb = function( v_) | ||
| 256 | local tv = type( v_) | ||
| 257 | return (tv == "function") and v_ or raise_option_error( "gc_cb", tv, v_) | ||
| 288 | end | 258 | end |
| 289 | end | 259 | } |
| 290 | 260 | ||
| 291 | -- the last argument should be a function or a string | 261 | -- PUBLIC LANES API |
| 292 | local func = select( n, ...) | 262 | -- receives a sequence of strings and tables, plus a function |
| 293 | local functype = type( func) | 263 | local gen = function( ...) |
| 294 | if functype ~= "function" and functype ~= "string" then | 264 | -- aggregrate all strings together, separated by "," as well as tables |
| 295 | error( "Last parameter not function or string: " .. functype .. " " .. string_format( "%q", tostring( func)), 2) | 265 | -- the strings are a list of libraries to open |
| 296 | end | 266 | -- the tables contain the lane options |
| 267 | local opt = {} | ||
| 268 | local libs = nil | ||
| 269 | |||
| 270 | local n = select( '#', ...) | ||
| 271 | |||
| 272 | -- we need at least a function | ||
| 273 | if n == 0 then | ||
| 274 | error( "No parameters!", 2) | ||
| 275 | end | ||
| 297 | 276 | ||
| 298 | -- check that the caller only provides reserved library names, and those only once | 277 | -- all arguments but the last must be nil, strings, or tables |
| 299 | -- "*" is a special case that doesn't require individual checking | 278 | for i = 1, n - 1 do |
| 300 | if libs and libs ~= "*" then | 279 | local v = select( i, ...) |
| 301 | local found = {} | 280 | local tv = type( v) |
| 302 | for s in string_gmatch(libs, "[%a%d.]+") do | 281 | if tv == "string" then |
| 303 | if not valid_libs[s] then | 282 | libs = libs and libs .. "," .. v or v |
| 304 | error( "Bad library name: " .. s, 2) | 283 | elseif tv == "table" then |
| 284 | for k, vv in pairs( v) do | ||
| 285 | opt[k]= vv | ||
| 286 | end | ||
| 287 | elseif v == nil then | ||
| 288 | -- skip | ||
| 305 | else | 289 | else |
| 306 | found[s] = (found[s] or 0) + 1 | 290 | error( "Bad parameter " .. i .. ": " .. tv .. " " .. string_format( "%q", tostring( v)), 2) |
| 307 | if found[s] > 1 then | 291 | end |
| 308 | error( "libs specification contains '" .. s .. "' more than once", 2) | 292 | end |
| 293 | |||
| 294 | -- the last argument should be a function or a string | ||
| 295 | local func = select( n, ...) | ||
| 296 | local functype = type( func) | ||
| 297 | if functype ~= "function" and functype ~= "string" then | ||
| 298 | error( "Last parameter not function or string: " .. functype .. " " .. string_format( "%q", tostring( func)), 2) | ||
| 299 | end | ||
| 300 | |||
| 301 | -- check that the caller only provides reserved library names, and those only once | ||
| 302 | -- "*" is a special case that doesn't require individual checking | ||
| 303 | if libs and libs ~= "*" then | ||
| 304 | local found = {} | ||
| 305 | for s in string_gmatch(libs, "[%a%d.]+") do | ||
| 306 | if not valid_libs[s] then | ||
| 307 | error( "Bad library name: " .. s, 2) | ||
| 308 | else | ||
| 309 | found[s] = (found[s] or 0) + 1 | ||
| 310 | if found[s] > 1 then | ||
| 311 | error( "libs specification contains '" .. s .. "' more than once", 2) | ||
| 312 | end | ||
| 309 | end | 313 | end |
| 310 | end | 314 | end |
| 311 | end | 315 | end |
| 312 | end | ||
| 313 | 316 | ||
| 314 | -- validate that each option is known and properly valued | 317 | -- validate that each option is known and properly valued |
| 315 | for k, v in pairs( opt) do | 318 | for k, v in pairs( opt) do |
| 316 | local validator = opt_validators[k] | 319 | local validator = opt_validators[k] |
| 317 | if not validator then | 320 | if not validator then |
| 318 | error( (type( k) == "number" and "Unkeyed option: " .. type( v) .. " " .. string_format( "%q", tostring( v)) or "Bad '" .. tostring( k) .. "' option"), 2) | 321 | error( (type( k) == "number" and "Unkeyed option: " .. type( v) .. " " .. string_format( "%q", tostring( v)) or "Bad '" .. tostring( k) .. "' option"), 2) |
| 319 | else | 322 | else |
| 320 | opt[k] = validator( v) | 323 | opt[k] = validator( v) |
| 324 | end | ||
| 321 | end | 325 | end |
| 322 | end | ||
| 323 | 326 | ||
| 324 | local cancelstep, priority, globals, package, required, gc_cb = opt.cancelstep, opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb | 327 | local cancelstep, priority, globals, package, required, gc_cb = opt.cancelstep, opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb |
| 325 | return function( ...) | 328 | return function( ...) |
| 326 | -- must pass functions args last else they will be truncated to the first one | 329 | -- must pass functions args last else they will be truncated to the first one |
| 327 | return core_lane_new( func, libs, cancelstep, priority, globals, package, required, gc_cb, ...) | 330 | return core_lane_new( func, libs, cancelstep, priority, globals, package, required, gc_cb, ...) |
| 328 | end | 331 | end |
| 329 | end -- gen() | 332 | end -- gen() |
| 330 | 333 | ||
| 331 | ---=== Timers ===--- | 334 | ---=== Timers ===--- |
| 332 | 335 | ||
| 333 | -- PUBLIC LANES API | 336 | -- PUBLIC LANES API |
| 334 | local timer = function() error "timers are not active" end | 337 | local timer = function() error "timers are not active" end |
| 335 | local timers = timer | 338 | local timers = timer |
| 336 | local timer_lane = nil | 339 | local timer_lane = nil |
| 337 | 340 | ||
| 338 | -- timer_gateway should always exist, even when the settings disable the timers | 341 | -- timer_gateway should always exist, even when the settings disable the timers |
| 339 | local timer_gateway = assert( core.timer_gateway) | 342 | local timer_gateway = assert( core.timer_gateway) |
| 340 | 343 | ||
| 341 | ----- | 344 | ----- |
| 342 | -- <void> = sleep( [seconds_]) | 345 | -- <void> = sleep( [seconds_]) |
| 343 | -- | 346 | -- |
| 344 | -- PUBLIC LANES API | 347 | -- PUBLIC LANES API |
| 345 | local sleep = function( seconds_) | 348 | local sleep = function( seconds_) |
| 346 | seconds_ = seconds_ or 0.0 -- this causes false and nil to be a valid input, equivalent to 0.0, but that's ok | 349 | seconds_ = seconds_ or 0.0 -- this causes false and nil to be a valid input, equivalent to 0.0, but that's ok |
| 347 | if type( seconds_) ~= "number" then | 350 | if type( seconds_) ~= "number" then |
| 348 | error( "invalid duration " .. string_format( "%q", tostring(seconds_))) | 351 | error( "invalid duration " .. string_format( "%q", tostring(seconds_))) |
| 352 | end | ||
| 353 | -- receive data on a channel no-one ever sends anything, thus blocking for the specified duration | ||
| 354 | return timer_gateway:receive( seconds_, "ac100de1-a696-4619-b2f0-a26de9d58ab8") | ||
| 349 | end | 355 | end |
| 350 | -- receive data on a channel no-one ever sends anything, thus blocking for the specified duration | ||
| 351 | return timer_gateway:receive( seconds_, "ac100de1-a696-4619-b2f0-a26de9d58ab8") | ||
| 352 | end | ||
| 353 | 356 | ||
| 354 | 357 | ||
| 355 | if settings.with_timers ~= false then | 358 | if settings.with_timers ~= false then |
| 356 | 359 | ||
| 357 | -- | 360 | -- |
| 358 | -- On first 'require "lanes"', a timer lane is spawned that will maintain | 361 | -- On first 'require "lanes"', a timer lane is spawned that will maintain |
| 359 | -- timer tables and sleep in between the timer events. All interaction with | 362 | -- timer tables and sleep in between the timer events. All interaction with |
| 360 | -- the timer lane happens via a 'timer_gateway' Linda, which is common to | 363 | -- the timer lane happens via a 'timer_gateway' Linda, which is common to |
| 361 | -- all that 'require "lanes"'. | 364 | -- all that 'require "lanes"'. |
| 362 | -- | 365 | -- |
| 363 | -- Linda protocol to timer lane: | 366 | -- Linda protocol to timer lane: |
| 364 | -- | 367 | -- |
| 365 | -- TGW_KEY: linda_h, key, [wakeup_at_secs], [repeat_secs] | 368 | -- TGW_KEY: linda_h, key, [wakeup_at_secs], [repeat_secs] |
| 366 | -- | 369 | -- |
| 367 | local TGW_KEY= "(timer control)" -- the key does not matter, a 'weird' key may help debugging | 370 | local TGW_KEY= "(timer control)" -- the key does not matter, a 'weird' key may help debugging |
| 368 | local TGW_QUERY, TGW_REPLY = "(timer query)", "(timer reply)" | 371 | local TGW_QUERY, TGW_REPLY = "(timer query)", "(timer reply)" |
| 369 | local first_time_key= "first time" | 372 | local first_time_key= "first time" |
| 370 | |||
| 371 | local first_time = timer_gateway:get( first_time_key) == nil | ||
| 372 | timer_gateway:set( first_time_key, true) | ||
| 373 | 373 | ||
| 374 | -- | 374 | local first_time = timer_gateway:get( first_time_key) == nil |
| 375 | -- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally | 375 | timer_gateway:set( first_time_key, true) |
| 376 | -- has 'table' always declared) | ||
| 377 | -- | ||
| 378 | if first_time then | ||
| 379 | 376 | ||
| 380 | local now_secs = core.now_secs | ||
| 381 | assert( type( now_secs) == "function") | ||
| 382 | ----- | ||
| 383 | -- Snore loop (run as a lane on the background) | ||
| 384 | -- | ||
| 385 | -- High priority, to get trustworthy timings. | ||
| 386 | -- | 377 | -- |
| 387 | -- We let the timer lane be a "free running" thread; no handle to it | 378 | -- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally |
| 388 | -- remains. | 379 | -- has 'table' always declared) |
| 389 | -- | 380 | -- |
| 390 | local timer_body = function() | 381 | if first_time then |
| 391 | set_debug_threadname( "LanesTimer") | 382 | |
| 392 | -- | 383 | local now_secs = core.now_secs |
| 393 | -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h, | 384 | assert( type( now_secs) == "function") |
| 394 | -- [key]= { wakeup_secs [,period_secs] } [, ...] }, | 385 | ----- |
| 395 | -- } | 386 | -- Snore loop (run as a lane on the background) |
| 396 | -- | ||
| 397 | -- Collection of all running timers, indexed with linda's & key. | ||
| 398 | -- | 387 | -- |
| 399 | -- Note that we need to use the deep lightuserdata identifiers, instead | 388 | -- High priority, to get trustworthy timings. |
| 400 | -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple | ||
| 401 | -- entries for the same timer. | ||
| 402 | -- | 389 | -- |
| 403 | -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but | 390 | -- We let the timer lane be a "free running" thread; no handle to it |
| 404 | -- also important to keep the Linda alive, even if all outside world threw | 391 | -- remains. |
| 405 | -- away pointers to it (which would ruin uniqueness of the deep pointer). | ||
| 406 | -- Now we're safe. | ||
| 407 | -- | 392 | -- |
| 408 | local collection = {} | 393 | local timer_body = function() |
| 409 | local table_insert = assert( table.insert) | 394 | set_debug_threadname( "LanesTimer") |
| 410 | 395 | -- | |
| 411 | local get_timers = function() | 396 | -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h, |
| 412 | local r = {} | 397 | -- [key]= { wakeup_secs [,period_secs] } [, ...] }, |
| 413 | for deep, t in pairs( collection) do | 398 | -- } |
| 414 | -- WR( tostring( deep)) | 399 | -- |
| 415 | local l = t[deep] | 400 | -- Collection of all running timers, indexed with linda's & key. |
| 416 | for key, timer_data in pairs( t) do | 401 | -- |
| 417 | if key ~= deep then | 402 | -- Note that we need to use the deep lightuserdata identifiers, instead |
| 418 | table_insert( r, {l, key, timer_data}) | 403 | -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple |
| 404 | -- entries for the same timer. | ||
| 405 | -- | ||
| 406 | -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but | ||
| 407 | -- also important to keep the Linda alive, even if all outside world threw | ||
| 408 | -- away pointers to it (which would ruin uniqueness of the deep pointer). | ||
| 409 | -- Now we're safe. | ||
| 410 | -- | ||
| 411 | local collection = {} | ||
| 412 | local table_insert = assert( table.insert) | ||
| 413 | |||
| 414 | local get_timers = function() | ||
| 415 | local r = {} | ||
| 416 | for deep, t in pairs( collection) do | ||
| 417 | -- WR( tostring( deep)) | ||
| 418 | local l = t[deep] | ||
| 419 | for key, timer_data in pairs( t) do | ||
| 420 | if key ~= deep then | ||
| 421 | table_insert( r, {l, key, timer_data}) | ||
| 422 | end | ||
| 419 | end | 423 | end |
| 420 | end | 424 | end |
| 421 | end | 425 | return r |
| 422 | return r | 426 | end -- get_timers() |
| 423 | end -- get_timers() | ||
| 424 | 427 | ||
| 425 | -- | 428 | -- |
| 426 | -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] ) | 429 | -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] ) |
| 427 | -- | 430 | -- |
| 428 | local set_timer = function( linda, key, wakeup_at, period) | 431 | local set_timer = function( linda, key, wakeup_at, period) |
| 429 | assert( wakeup_at == nil or wakeup_at > 0.0) | 432 | assert( wakeup_at == nil or wakeup_at > 0.0) |
| 430 | assert( period == nil or period > 0.0) | 433 | assert( period == nil or period > 0.0) |
| 431 | 434 | ||
| 432 | local linda_deep = linda:deep() | 435 | local linda_deep = linda:deep() |
| 433 | assert( linda_deep) | 436 | assert( linda_deep) |
| 434 | 437 | ||
| 435 | -- Find or make a lookup for this timer | 438 | -- Find or make a lookup for this timer |
| 436 | -- | ||
| 437 | local t1 = collection[linda_deep] | ||
| 438 | if not t1 then | ||
| 439 | t1 = { [linda_deep] = linda} -- proxy to use the Linda | ||
| 440 | collection[linda_deep] = t1 | ||
| 441 | end | ||
| 442 | |||
| 443 | if wakeup_at == nil then | ||
| 444 | -- Clear the timer | ||
| 445 | -- | 439 | -- |
| 446 | t1[key]= nil | 440 | local t1 = collection[linda_deep] |
| 441 | if not t1 then | ||
| 442 | t1 = { [linda_deep] = linda} -- proxy to use the Linda | ||
| 443 | collection[linda_deep] = t1 | ||
| 444 | end | ||
| 445 | |||
| 446 | if wakeup_at == nil then | ||
| 447 | -- Clear the timer | ||
| 448 | -- | ||
| 449 | t1[key]= nil | ||
| 447 | 450 | ||
| 448 | -- Remove empty tables from collection; speeds timer checks and | 451 | -- Remove empty tables from collection; speeds timer checks and |
| 449 | -- lets our 'safety reference' proxy be gc:ed as well. | 452 | -- lets our 'safety reference' proxy be gc:ed as well. |
| 450 | -- | 453 | -- |
| 451 | local empty = true | 454 | local empty = true |
| 452 | for k, _ in pairs( t1) do | 455 | for k, _ in pairs( t1) do |
| 453 | if k ~= linda_deep then | 456 | if k ~= linda_deep then |
| 454 | empty = false | 457 | empty = false |
| 455 | break | 458 | break |
| 459 | end | ||
| 460 | end | ||
| 461 | if empty then | ||
| 462 | collection[linda_deep] = nil | ||
| 456 | end | 463 | end |
| 464 | |||
| 465 | -- Note: any unread timer value is left at 'linda[key]' intensionally; | ||
| 466 | -- clearing a timer just stops it. | ||
| 467 | else | ||
| 468 | -- New timer or changing the timings | ||
| 469 | -- | ||
| 470 | local t2 = t1[key] | ||
| 471 | if not t2 then | ||
| 472 | t2= {} | ||
| 473 | t1[key]= t2 | ||
| 474 | end | ||
| 475 | |||
| 476 | t2[1] = wakeup_at | ||
| 477 | t2[2] = period -- can be 'nil' | ||
| 457 | end | 478 | end |
| 458 | if empty then | 479 | end -- set_timer() |
| 459 | collection[linda_deep] = nil | 480 | |
| 481 | ----- | ||
| 482 | -- [next_wakeup_at]= check_timers() | ||
| 483 | -- Check timers, and wake up the ones expired (if any) | ||
| 484 | -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none). | ||
| 485 | local check_timers = function() | ||
| 486 | local now = now_secs() | ||
| 487 | local next_wakeup | ||
| 488 | |||
| 489 | for linda_deep,t1 in pairs(collection) do | ||
| 490 | for key,t2 in pairs(t1) do | ||
| 491 | -- | ||
| 492 | if key==linda_deep then | ||
| 493 | -- no 'continue' in Lua :/ | ||
| 494 | else | ||
| 495 | -- 't2': { wakeup_at_secs [,period_secs] } | ||
| 496 | -- | ||
| 497 | local wakeup_at= t2[1] | ||
| 498 | local period= t2[2] -- may be 'nil' | ||
| 499 | |||
| 500 | if wakeup_at <= now then | ||
| 501 | local linda= t1[linda_deep] | ||
| 502 | assert(linda) | ||
| 503 | |||
| 504 | linda:set( key, now ) | ||
| 505 | |||
| 506 | -- 'pairs()' allows the values to be modified (and even | ||
| 507 | -- removed) as far as keys are not touched | ||
| 508 | |||
| 509 | if not period then | ||
| 510 | -- one-time timer; gone | ||
| 511 | -- | ||
| 512 | t1[key]= nil | ||
| 513 | wakeup_at= nil -- no 'continue' in Lua :/ | ||
| 514 | else | ||
| 515 | -- repeating timer; find next wakeup (may jump multiple repeats) | ||
| 516 | -- | ||
| 517 | repeat | ||
| 518 | wakeup_at= wakeup_at+period | ||
| 519 | until wakeup_at > now | ||
| 520 | |||
| 521 | t2[1]= wakeup_at | ||
| 522 | end | ||
| 523 | end | ||
| 524 | |||
| 525 | if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then | ||
| 526 | next_wakeup= wakeup_at | ||
| 527 | end | ||
| 528 | end | ||
| 529 | end -- t2 loop | ||
| 530 | end -- t1 loop | ||
| 531 | |||
| 532 | return next_wakeup -- may be 'nil' | ||
| 533 | end -- check_timers() | ||
| 534 | |||
| 535 | local timer_gateway_batched = timer_gateway.batched | ||
| 536 | set_finalizer( function( err, stk) | ||
| 537 | if err and type( err) ~= "userdata" then | ||
| 538 | WR( "LanesTimer error: "..tostring(err)) | ||
| 539 | --elseif type( err) == "userdata" then | ||
| 540 | -- WR( "LanesTimer after cancel" ) | ||
| 541 | --else | ||
| 542 | -- WR("LanesTimer finalized") | ||
| 460 | end | 543 | end |
| 544 | end) | ||
| 545 | while true do | ||
| 546 | local next_wakeup = check_timers() | ||
| 461 | 547 | ||
| 462 | -- Note: any unread timer value is left at 'linda[key]' intensionally; | 548 | -- Sleep until next timer to wake up, or a set/clear command |
| 463 | -- clearing a timer just stops it. | ||
| 464 | else | ||
| 465 | -- New timer or changing the timings | ||
| 466 | -- | 549 | -- |
| 467 | local t2 = t1[key] | 550 | local secs |
| 468 | if not t2 then | 551 | if next_wakeup then |
| 469 | t2= {} | 552 | secs = next_wakeup - now_secs() |
| 470 | t1[key]= t2 | 553 | if secs < 0 then secs = 0 end |
| 471 | end | 554 | end |
| 472 | 555 | local key, what = timer_gateway:receive( secs, TGW_KEY, TGW_QUERY) | |
| 473 | t2[1] = wakeup_at | 556 | |
| 474 | t2[2] = period -- can be 'nil' | 557 | if key == TGW_KEY then |
| 475 | end | 558 | assert( getmetatable( what) == "Linda") -- 'what' should be a linda on which the client sets a timer |
| 476 | end -- set_timer() | 559 | local _, key, wakeup_at, period = timer_gateway:receive( 0, timer_gateway_batched, TGW_KEY, 3) |
| 477 | 560 | assert( key) | |
| 478 | ----- | 561 | set_timer( what, key, wakeup_at, period and period > 0 and period or nil) |
| 479 | -- [next_wakeup_at]= check_timers() | 562 | elseif key == TGW_QUERY then |
| 480 | -- Check timers, and wake up the ones expired (if any) | 563 | if what == "get_timers" then |
| 481 | -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none). | 564 | timer_gateway:send( TGW_REPLY, get_timers()) |
| 482 | local check_timers = function() | ||
| 483 | local now = now_secs() | ||
| 484 | local next_wakeup | ||
| 485 | |||
| 486 | for linda_deep,t1 in pairs(collection) do | ||
| 487 | for key,t2 in pairs(t1) do | ||
| 488 | -- | ||
| 489 | if key==linda_deep then | ||
| 490 | -- no 'continue' in Lua :/ | ||
| 491 | else | 565 | else |
| 492 | -- 't2': { wakeup_at_secs [,period_secs] } | 566 | timer_gateway:send( TGW_REPLY, "unknown query " .. what) |
| 493 | -- | ||
| 494 | local wakeup_at= t2[1] | ||
| 495 | local period= t2[2] -- may be 'nil' | ||
| 496 | |||
| 497 | if wakeup_at <= now then | ||
| 498 | local linda= t1[linda_deep] | ||
| 499 | assert(linda) | ||
| 500 | |||
| 501 | linda:set( key, now ) | ||
| 502 | |||
| 503 | -- 'pairs()' allows the values to be modified (and even | ||
| 504 | -- removed) as far as keys are not touched | ||
| 505 | |||
| 506 | if not period then | ||
| 507 | -- one-time timer; gone | ||
| 508 | -- | ||
| 509 | t1[key]= nil | ||
| 510 | wakeup_at= nil -- no 'continue' in Lua :/ | ||
| 511 | else | ||
| 512 | -- repeating timer; find next wakeup (may jump multiple repeats) | ||
| 513 | -- | ||
| 514 | repeat | ||
| 515 | wakeup_at= wakeup_at+period | ||
| 516 | until wakeup_at > now | ||
| 517 | |||
| 518 | t2[1]= wakeup_at | ||
| 519 | end | ||
| 520 | end | ||
| 521 | |||
| 522 | if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then | ||
| 523 | next_wakeup= wakeup_at | ||
| 524 | end | ||
| 525 | end | 567 | end |
| 526 | end -- t2 loop | 568 | --elseif secs == nil then -- got no value while block-waiting? |
| 527 | end -- t1 loop | 569 | -- WR( "timer lane: no linda, aborted?") |
| 528 | 570 | end | |
| 529 | return next_wakeup -- may be 'nil' | ||
| 530 | end -- check_timers() | ||
| 531 | |||
| 532 | local timer_gateway_batched = timer_gateway.batched | ||
| 533 | set_finalizer( function( err, stk) | ||
| 534 | if err and type( err) ~= "userdata" then | ||
| 535 | WR( "LanesTimer error: "..tostring(err)) | ||
| 536 | --elseif type( err) == "userdata" then | ||
| 537 | -- WR( "LanesTimer after cancel" ) | ||
| 538 | --else | ||
| 539 | -- WR("LanesTimer finalized") | ||
| 540 | end | 571 | end |
| 541 | end) | 572 | end -- timer_body() |
| 542 | while true do | 573 | timer_lane = gen( "*", { package= {}, priority = max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility... |
| 543 | local next_wakeup = check_timers() | 574 | end -- first_time |
| 544 | 575 | ||
| 545 | -- Sleep until next timer to wake up, or a set/clear command | 576 | ----- |
| 577 | -- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) | ||
| 578 | -- | ||
| 579 | -- PUBLIC LANES API | ||
| 580 | timer = function( linda, key, a, period ) | ||
| 581 | if getmetatable( linda) ~= "Linda" then | ||
| 582 | error "expecting a Linda" | ||
| 583 | end | ||
| 584 | if a == 0.0 then | ||
| 585 | -- Caller expects to get current time stamp in Linda, on return | ||
| 586 | -- (like the timer had expired instantly); it would be good to set this | ||
| 587 | -- as late as possible (to give most current time) but also we want it | ||
| 588 | -- to precede any possible timers that might start striking. | ||
| 546 | -- | 589 | -- |
| 547 | local secs | 590 | linda:set( key, core.now_secs()) |
| 548 | if next_wakeup then | 591 | |
| 549 | secs = next_wakeup - now_secs() | 592 | if not period or period==0.0 then |
| 550 | if secs < 0 then secs = 0 end | 593 | timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer |
| 551 | end | 594 | return -- nothing more to do |
| 552 | local key, what = timer_gateway:receive( secs, TGW_KEY, TGW_QUERY) | ||
| 553 | |||
| 554 | if key == TGW_KEY then | ||
| 555 | assert( getmetatable( what) == "Linda") -- 'what' should be a linda on which the client sets a timer | ||
| 556 | local _, key, wakeup_at, period = timer_gateway:receive( 0, timer_gateway_batched, TGW_KEY, 3) | ||
| 557 | assert( key) | ||
| 558 | set_timer( what, key, wakeup_at, period and period > 0 and period or nil) | ||
| 559 | elseif key == TGW_QUERY then | ||
| 560 | if what == "get_timers" then | ||
| 561 | timer_gateway:send( TGW_REPLY, get_timers()) | ||
| 562 | else | ||
| 563 | timer_gateway:send( TGW_REPLY, "unknown query " .. what) | ||
| 564 | end | ||
| 565 | --elseif secs == nil then -- got no value while block-waiting? | ||
| 566 | -- WR( "timer lane: no linda, aborted?") | ||
| 567 | end | 595 | end |
| 596 | a= period | ||
| 568 | end | 597 | end |
| 569 | end -- timer_body() | ||
| 570 | timer_lane = gen( "*", { package= {}, priority = max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility... | ||
| 571 | end -- first_time | ||
| 572 | 598 | ||
| 573 | ----- | 599 | local wakeup_at= type(a)=="table" and core.wakeup_conv(a) -- given point of time |
| 574 | -- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) | 600 | or (a and core.now_secs()+a or nil) |
| 575 | -- | 601 | -- queue to timer |
| 576 | -- PUBLIC LANES API | 602 | -- |
| 577 | timer = function( linda, key, a, period ) | 603 | timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) |
| 578 | if getmetatable( linda) ~= "Linda" then | 604 | end |
| 579 | error "expecting a Linda" | ||
| 580 | end | ||
| 581 | if a == 0.0 then | ||
| 582 | -- Caller expects to get current time stamp in Linda, on return | ||
| 583 | -- (like the timer had expired instantly); it would be good to set this | ||
| 584 | -- as late as possible (to give most current time) but also we want it | ||
| 585 | -- to precede any possible timers that might start striking. | ||
| 586 | -- | ||
| 587 | linda:set( key, core.now_secs()) | ||
| 588 | |||
| 589 | if not period or period==0.0 then | ||
| 590 | timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer | ||
| 591 | return -- nothing more to do | ||
| 592 | end | ||
| 593 | a= period | ||
| 594 | end | ||
| 595 | |||
| 596 | local wakeup_at= type(a)=="table" and core.wakeup_conv(a) -- given point of time | ||
| 597 | or (a and core.now_secs()+a or nil) | ||
| 598 | -- queue to timer | ||
| 599 | -- | ||
| 600 | timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) | ||
| 601 | end | ||
| 602 | 605 | ||
| 603 | ----- | 606 | ----- |
| 604 | -- {[{linda, slot, when, period}[,...]]} = timers() | 607 | -- {[{linda, slot, when, period}[,...]]} = timers() |
| 605 | -- | 608 | -- |
| 606 | -- PUBLIC LANES API | 609 | -- PUBLIC LANES API |
| 607 | timers = function() | 610 | timers = function() |
| 608 | timer_gateway:send( TGW_QUERY, "get_timers") | 611 | timer_gateway:send( TGW_QUERY, "get_timers") |
| 609 | local _, r = timer_gateway:receive( TGW_REPLY) | 612 | local _, r = timer_gateway:receive( TGW_REPLY) |
| 610 | return r | 613 | return r |
| 611 | end | 614 | end |
| 612 | 615 | ||
| 613 | end -- settings.with_timers | 616 | end -- settings.with_timers |
| 614 | 617 | ||
| 615 | -- avoid pulling the whole core module as upvalue when cancel_error is enough | 618 | -- avoid pulling the whole core module as upvalue when cancel_error is enough |
| 616 | local cancel_error = assert( core.cancel_error) | 619 | local cancel_error = assert( core.cancel_error) |
| 617 | 620 | ||
| 618 | ---=== Lock & atomic generators ===--- | 621 | ---=== Lock & atomic generators ===--- |
| 619 | 622 | ||
| 620 | -- These functions are just surface sugar, but make solutions easier to read. | 623 | -- These functions are just surface sugar, but make solutions easier to read. |
| 621 | -- Not many applications should even need explicit locks or atomic counters. | 624 | -- Not many applications should even need explicit locks or atomic counters. |
| 622 | 625 | ||
| 623 | -- | 626 | -- |
| 624 | -- [true [, ...]= trues(uint) | 627 | -- [true [, ...]= trues(uint) |
| 625 | -- | 628 | -- |
| 626 | local function trues( n) | 629 | local function trues( n) |
| 627 | if n > 0 then | 630 | if n > 0 then |
| 628 | return true, trues( n - 1) | 631 | return true, trues( n - 1) |
| 632 | end | ||
| 629 | end | 633 | end |
| 630 | end | ||
| 631 | 634 | ||
| 632 | -- | 635 | -- |
| 633 | -- lock_f = lanes.genlock( linda_h, key [,N_uint=1] ) | 636 | -- lock_f = lanes.genlock( linda_h, key [,N_uint=1] ) |
| 634 | -- | 637 | -- |
| 635 | -- = lock_f( +M ) -- acquire M | 638 | -- = lock_f( +M ) -- acquire M |
| 636 | -- ...locked... | 639 | -- ...locked... |
| 637 | -- = lock_f( -M ) -- release M | 640 | -- = lock_f( -M ) -- release M |
| 638 | -- | 641 | -- |
| 639 | -- Returns an access function that allows 'N' simultaneous entries between | 642 | -- Returns an access function that allows 'N' simultaneous entries between |
| 640 | -- acquire (+M) and release (-M). For binary locks, use M==1. | 643 | -- acquire (+M) and release (-M). For binary locks, use M==1. |
| 641 | -- | 644 | -- |
| 642 | -- PUBLIC LANES API | 645 | -- PUBLIC LANES API |
| 643 | local genlock = function( linda, key, N) | 646 | local genlock = function( linda, key, N) |
| 644 | -- clear existing data and set the limit | 647 | -- clear existing data and set the limit |
| 645 | N = N or 1 | 648 | N = N or 1 |
| 646 | if linda:set( key) == cancel_error or linda:limit( key, N) == cancel_error then | 649 | if linda:set( key) == cancel_error or linda:limit( key, N) == cancel_error then |
| 647 | return cancel_error | 650 | return cancel_error |
| 648 | end | 651 | end |
| 649 | 652 | ||
| 650 | -- use an optimized version for case N == 1 | 653 | -- use an optimized version for case N == 1 |
| 651 | return (N == 1) and | 654 | return (N == 1) and |
| 652 | function( M, mode_) | 655 | function( M, mode_) |
| 653 | local timeout = (mode_ == "try") and 0 or nil | 656 | local timeout = (mode_ == "try") and 0 or nil |
| 654 | if M > 0 then | 657 | if M > 0 then |
| 655 | -- 'nil' timeout allows 'key' to be numeric | 658 | -- 'nil' timeout allows 'key' to be numeric |
| 656 | return linda:send( timeout, key, true) -- suspends until been able to push them | 659 | return linda:send( timeout, key, true) -- suspends until been able to push them |
| 657 | else | 660 | else |
| 658 | local k = linda:receive( nil, key) | 661 | local k = linda:receive( nil, key) |
| 659 | -- propagate cancel_error if we got it, else return true or false | 662 | -- propagate cancel_error if we got it, else return true or false |
| 660 | return k and ((k ~= cancel_error) and true or k) or false | 663 | return k and ((k ~= cancel_error) and true or k) or false |
| 664 | end | ||
| 661 | end | 665 | end |
| 662 | end | 666 | or |
| 663 | or | 667 | function( M, mode_) |
| 664 | function( M, mode_) | 668 | local timeout = (mode_ == "try") and 0 or nil |
| 665 | local timeout = (mode_ == "try") and 0 or nil | 669 | if M > 0 then |
| 666 | if M > 0 then | 670 | -- 'nil' timeout allows 'key' to be numeric |
| 667 | -- 'nil' timeout allows 'key' to be numeric | 671 | return linda:send( timeout, key, trues(M)) -- suspends until been able to push them |
| 668 | return linda:send( timeout, key, trues(M)) -- suspends until been able to push them | 672 | else |
| 669 | else | 673 | local k = linda:receive( nil, linda.batched, key, -M) |
| 670 | local k = linda:receive( nil, linda.batched, key, -M) | 674 | -- propagate cancel_error if we got it, else return true or false |
| 671 | -- propagate cancel_error if we got it, else return true or false | 675 | return k and ((k ~= cancel_error) and true or k) or false |
| 672 | return k and ((k ~= cancel_error) and true or k) or false | 676 | end |
| 673 | end | 677 | end |
| 674 | end | 678 | end |
| 675 | end | ||
| 676 | 679 | ||
| 677 | 680 | ||
| 678 | -- | 681 | -- |
| @@ -728,6 +731,13 @@ end | |||
| 728 | return lanes | 731 | return lanes |
| 729 | end -- lanes.configure | 732 | end -- lanes.configure |
| 730 | 733 | ||
| 734 | lanesMeta.__index = function(t,k) | ||
| 735 | -- This is called when some functionality is accessed without calling configure() | ||
| 736 | lanes.configure() -- Initialize with default settings | ||
| 737 | -- Access the required key | ||
| 738 | return lanes[k] | ||
| 739 | end | ||
| 740 | |||
| 731 | -- no need to force calling configure() excepted the first time | 741 | -- no need to force calling configure() excepted the first time |
| 732 | if core.settings then | 742 | if core.settings then |
| 733 | return lanes.configure() | 743 | return lanes.configure() |
| @@ -735,5 +745,6 @@ else | |||
| 735 | return lanes | 745 | return lanes |
| 736 | end | 746 | end |
| 737 | 747 | ||
| 738 | --the end | 748 | |
| 739 | return lanes | 749 | --the end (Unreachable Code) |
| 750 | --return lanes | ||
