aboutsummaryrefslogtreecommitdiff
path: root/src/lanes.lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/lanes.lua')
-rw-r--r--src/lanes.lua323
1 files changed, 163 insertions, 160 deletions
diff --git a/src/lanes.lua b/src/lanes.lua
index 1d50f97..f4eef81 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -123,9 +123,6 @@ lanes.configure = function( _params)
123 123
124 local set_singlethreaded = assert( core.set_singlethreaded) 124 local set_singlethreaded = assert( core.set_singlethreaded)
125 125
126 local now_secs = assert( core.now_secs)
127 local wakeup_conv = assert( core.wakeup_conv)
128
129 local max_prio = assert( core.max_prio) 126 local max_prio = assert( core.max_prio)
130 127
131lanes.ABOUT= 128lanes.ABOUT=
@@ -311,6 +308,7 @@ local linda = core.linda
311 308
312-- PUBLIC LANES API 309-- PUBLIC LANES API
313local timer = function() error "timers are not active" end 310local timer = function() error "timers are not active" end
311local timer_lane = nil
314local timers = timer 312local timers = timer
315 313
316if _params.with_timers ~= false then 314if _params.with_timers ~= false then
@@ -338,158 +336,161 @@ timer_gateway:set(first_time_key,true)
338-- has 'table' always declared) 336-- has 'table' always declared)
339-- 337--
340if first_time then 338if first_time then
341 local table_remove= assert( table.remove )
342 local table_insert= assert( table.insert )
343 339
344 -- 340 -----
345 -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h, 341 -- Snore loop (run as a lane on the background)
346 -- [key]= { wakeup_secs [,period_secs] } [, ...] }, 342 --
347 -- } 343 -- High priority, to get trustworthy timings.
348 -- 344 --
349 -- Collection of all running timers, indexed with linda's & key. 345 -- We let the timer lane be a "free running" thread; no handle to it
350 -- 346 -- remains.
351 -- Note that we need to use the deep lightuserdata identifiers, instead 347 --
352 -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple 348 local timer_body = function()
353 -- entries for the same timer. 349 -- require lanes.core inside the timer body to prevent pulling now_secs() through an uvpvalue
354 -- 350 local core = require "lanes.core"
355 -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but 351
356 -- also important to keep the Linda alive, even if all outside world threw 352 --
357 -- away pointers to it (which would ruin uniqueness of the deep pointer). 353 -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h,
358 -- Now we're safe. 354 -- [key]= { wakeup_secs [,period_secs] } [, ...] },
359 -- 355 -- }
360 local collection= {} 356 --
361 357 -- Collection of all running timers, indexed with linda's & key.
362 local function get_timers() 358 --
363 local r = {} 359 -- Note that we need to use the deep lightuserdata identifiers, instead
364 for deep, t in pairs( collection) do 360 -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple
365 -- WR( tostring( deep)) 361 -- entries for the same timer.
366 local l = t[deep] 362 --
367 for key, timer_data in pairs( t) do 363 -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but
368 if key ~= deep then 364 -- also important to keep the Linda alive, even if all outside world threw
369 table_insert( r, {l, key, timer_data}) 365 -- away pointers to it (which would ruin uniqueness of the deep pointer).
370 end 366 -- Now we're safe.
371 end 367 --
372 end 368 local collection = {}
373 return r 369 local table_insert = assert( table.insert)
374 end 370
375 -- 371 local get_timers = function()
376 -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] ) 372 local r = {}
377 -- 373 for deep, t in pairs( collection) do
378 local function set_timer( linda, key, wakeup_at, period ) 374 -- WR( tostring( deep))
379 assert( wakeup_at==nil or wakeup_at>0.0 ) 375 local l = t[deep]
380 assert( period==nil or period>0.0 ) 376 for key, timer_data in pairs( t) do
377 if key ~= deep then
378 table_insert( r, {l, key, timer_data})
379 end
380 end
381 end
382 return r
383 end -- get_timers()
381 384
382 local linda_deep= linda:deep() 385 --
383 assert( linda_deep ) 386 -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] )
387 --
388 local set_timer = function( linda, key, wakeup_at, period)
389 assert( wakeup_at == nil or wakeup_at > 0.0)
390 assert( period == nil or period > 0.0)
384 391
385 -- Find or make a lookup for this timer 392 local linda_deep = linda:deep()
386 -- 393 assert( linda_deep)
387 local t1= collection[linda_deep] 394
388 if not t1 then 395 -- Find or make a lookup for this timer
389 t1= { [linda_deep]= linda } -- proxy to use the Linda 396 --
390 collection[linda_deep]= t1 397 local t1 = collection[linda_deep]
391 end 398 if not t1 then
392 399 t1 = { [linda_deep] = linda} -- proxy to use the Linda
393 if wakeup_at==nil then 400 collection[linda_deep] = t1
394 -- Clear the timer 401 end
395 -- 402
396 t1[key]= nil 403 if wakeup_at == nil then
397 404 -- Clear the timer
398 -- Remove empty tables from collection; speeds timer checks and 405 --
399 -- lets our 'safety reference' proxy be gc:ed as well. 406 t1[key]= nil
400 -- 407
401 local empty= true 408 -- Remove empty tables from collection; speeds timer checks and
402 for k,_ in pairs(t1) do 409 -- lets our 'safety reference' proxy be gc:ed as well.
403 if k~= linda_deep then 410 --
404 empty= false; break 411 local empty = true
405 end 412 for k, _ in pairs( t1) do
406 end 413 if k ~= linda_deep then
407 if empty then 414 empty = false
408 collection[linda_deep]= nil 415 break
409 end 416 end
410 417 end
411 -- Note: any unread timer value is left at 'linda[key]' intensionally; 418 if empty then
412 -- clearing a timer just stops it. 419 collection[linda_deep] = nil
413 else 420 end
414 -- New timer or changing the timings 421
415 -- 422 -- Note: any unread timer value is left at 'linda[key]' intensionally;
416 local t2= t1[key] 423 -- clearing a timer just stops it.
417 if not t2 then 424 else
418 t2= {}; t1[key]= t2 425 -- New timer or changing the timings
419 end 426 --
420 427 local t2 = t1[key]
421 t2[1]= wakeup_at 428 if not t2 then
422 t2[2]= period -- can be 'nil' 429 t2= {}
423 end 430 t1[key]= t2
424 end 431 end
432
433 t2[1] = wakeup_at
434 t2[2] = period -- can be 'nil'
435 end
436 end -- set_timer()
437
438 local now_secs = core.now_secs
439 assert( type( now_secs) == "function")
440 -----
441 -- [next_wakeup_at]= check_timers()
442 -- Check timers, and wake up the ones expired (if any)
443 -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none).
444 local check_timers = function()
445 local now = now_secs()
446 local next_wakeup
447
448 for linda_deep,t1 in pairs(collection) do
449 for key,t2 in pairs(t1) do
450 --
451 if key==linda_deep then
452 -- no 'continue' in Lua :/
453 else
454 -- 't2': { wakeup_at_secs [,period_secs] }
455 --
456 local wakeup_at= t2[1]
457 local period= t2[2] -- may be 'nil'
458
459 if wakeup_at <= now then
460 local linda= t1[linda_deep]
461 assert(linda)
462
463 linda:set( key, now )
464
465 -- 'pairs()' allows the values to be modified (and even
466 -- removed) as far as keys are not touched
467
468 if not period then
469 -- one-time timer; gone
470 --
471 t1[key]= nil
472 wakeup_at= nil -- no 'continue' in Lua :/
473 else
474 -- repeating timer; find next wakeup (may jump multiple repeats)
475 --
476 repeat
477 wakeup_at= wakeup_at+period
478 until wakeup_at > now
479
480 t2[1]= wakeup_at
481 end
482 end
483
484 if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then
485 next_wakeup= wakeup_at
486 end
487 end
488 end -- t2 loop
489 end -- t1 loop
490
491 return next_wakeup -- may be 'nil'
492 end -- check_timers()
425 493
426 -----
427 -- [next_wakeup_at]= check_timers()
428 --
429 -- Check timers, and wake up the ones expired (if any)
430 --
431 -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none).
432 --
433 local function check_timers()
434
435 local now= now_secs()
436 local next_wakeup
437
438 for linda_deep,t1 in pairs(collection) do
439 for key,t2 in pairs(t1) do
440 --
441 if key==linda_deep then
442 -- no 'continue' in Lua :/
443 else
444 -- 't2': { wakeup_at_secs [,period_secs] }
445 --
446 local wakeup_at= t2[1]
447 local period= t2[2] -- may be 'nil'
448
449 if wakeup_at <= now then
450 local linda= t1[linda_deep]
451 assert(linda)
452
453 linda:set( key, now )
454
455 -- 'pairs()' allows the values to be modified (and even
456 -- removed) as far as keys are not touched
457
458 if not period then
459 -- one-time timer; gone
460 --
461 t1[key]= nil
462 wakeup_at= nil -- no 'continue' in Lua :/
463 else
464 -- repeating timer; find next wakeup (may jump multiple repeats)
465 --
466 repeat
467 wakeup_at= wakeup_at+period
468 until wakeup_at > now
469
470 t2[1]= wakeup_at
471 end
472 end
473
474 if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then
475 next_wakeup= wakeup_at
476 end
477 end
478 end -- t2 loop
479 end -- t1 loop
480
481 return next_wakeup -- may be 'nil'
482 end
483
484 -----
485 -- Snore loop (run as a lane on the background)
486 --
487 -- High priority, to get trustworthy timings.
488 --
489 -- We let the timer lane be a "free running" thread; no handle to it
490 -- remains.
491 --
492 local timer_body = function()
493 local timer_gateway_batched = timer_gateway.batched 494 local timer_gateway_batched = timer_gateway.batched
494 set_debug_threadname( "LanesTimer") 495 set_debug_threadname( "LanesTimer")
495 set_finalizer( function( err, stk) 496 set_finalizer( function( err, stk)
@@ -502,7 +503,7 @@ if first_time then
502 end 503 end
503 end) 504 end)
504 while true do 505 while true do
505 local next_wakeup= check_timers() 506 local next_wakeup = check_timers()
506 507
507 -- Sleep until next timer to wake up, or a set/clear command 508 -- Sleep until next timer to wake up, or a set/clear command
508 -- 509 --
@@ -528,9 +529,9 @@ if first_time then
528 -- WR( "timer lane: no linda, aborted?") 529 -- WR( "timer lane: no linda, aborted?")
529 end 530 end
530 end 531 end
531 end 532 end -- timer_body()
532 gen( "*", { package= {}, priority = max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility... 533 timer_lane = gen( "*", { package= {}, priority = max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility...
533end 534end -- first_time
534 535
535----- 536-----
536-- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) 537-- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] )
@@ -540,13 +541,13 @@ timer = function( linda, key, a, period )
540 if getmetatable( linda) ~= "Linda" then 541 if getmetatable( linda) ~= "Linda" then
541 error "expecting a Linda" 542 error "expecting a Linda"
542 end 543 end
543 if a==0.0 then 544 if a == 0.0 then
544 -- Caller expects to get current time stamp in Linda, on return 545 -- Caller expects to get current time stamp in Linda, on return
545 -- (like the timer had expired instantly); it would be good to set this 546 -- (like the timer had expired instantly); it would be good to set this
546 -- as late as possible (to give most current time) but also we want it 547 -- as late as possible (to give most current time) but also we want it
547 -- to precede any possible timers that might start striking. 548 -- to precede any possible timers that might start striking.
548 -- 549 --
549 linda:set( key, now_secs() ) 550 linda:set( key, core.now_secs())
550 551
551 if not period or period==0.0 then 552 if not period or period==0.0 then
552 timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer 553 timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer
@@ -555,8 +556,8 @@ timer = function( linda, key, a, period )
555 a= period 556 a= period
556 end 557 end
557 558
558 local wakeup_at= type(a)=="table" and wakeup_conv(a) -- given point of time 559 local wakeup_at= type(a)=="table" and core.wakeup_conv(a) -- given point of time
559 or (a and now_secs()+a or nil) 560 or (a and core.now_secs()+a or nil)
560 -- queue to timer 561 -- queue to timer
561 -- 562 --
562 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) 563 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period )
@@ -639,15 +640,17 @@ local function genatomic( linda, key, initial_val )
639end 640end
640 641
641 -- activate full interface 642 -- activate full interface
643 lanes.require = core.require
642 lanes.gen = gen 644 lanes.gen = gen
643 lanes.linda = core.linda 645 lanes.linda = core.linda
644 lanes.cancel_error = core.cancel_error 646 lanes.cancel_error = core.cancel_error
645 lanes.nameof = core.nameof 647 lanes.nameof = core.nameof
646 lanes.threads = (_params.track_lanes and core.threads) and core.threads or function() error "lane tracking is not available" end 648 lanes.threads = (_params.track_lanes and core.threads) and core.threads or function() error "lane tracking is not available" end
647 lanes.timer = timer 649 lanes.timer = timer
650 lanes.timer_lane = timer_lane
648 lanes.timers = timers 651 lanes.timers = timers
649 lanes.genlock = genlock 652 lanes.genlock = genlock
650 lanes.now_secs = now_secs 653 lanes.now_secs = core.now_secs
651 lanes.genatomic = genatomic 654 lanes.genatomic = genatomic
652 -- from now on, calling configure does nothing but checking that we don't call it with parameters that changed compared to the first invocation 655 -- from now on, calling configure does nothing but checking that we don't call it with parameters that changed compared to the first invocation
653 lanes.configure = function( _params2) 656 lanes.configure = function( _params2)