Skip to content

Instantly share code, notes, and snippets.

@remzmike
Last active September 24, 2018 16:07
Show Gist options
  • Save remzmike/67520522c98ba7828d07 to your computer and use it in GitHub Desktop.
Save remzmike/67520522c98ba7828d07 to your computer and use it in GitHub Desktop.
-- throttling/scheduling function wrappers
-- v01 - 4/26/2013 3:08:51 PM - tested in lua 5.1 and 5.2
-- todo: allow optional string keys
local _registry = {}
function run_many(count, fn, ...)
return internal_run({fn=fn, count=count}, ...)
end
function run_once(fn, ...)
return run_many(1, fn, ...)
end
function run_every(interval, fn, ...)
return internal_run({fn=fn, interval=interval}, ...)
end
function run_later(seconds, fn, ...)
return internal_run({fn=fn, count=1, start=os.clock()+seconds}, ...)
end
function run_at(clock, fn, ...)
return internal_run({fn=fn, count=1, start=clock}, ...)
end
-- technically optional, but recommended
-- run checks at different places in code, make code more clear
-- key is key or fn
-- if args are passed, then they are used in latest call, else original args are used
function run_check(key, ...)
local data = _registry[key]
if data==nil then
print('attempted run_check with invalid key', ':', key) -- todo: fixitup
end
local n = select('#', ...)
local result
if n>0 then
result = internal_run(data.t, ...)
else
result = internal_run(data.t, unpack(data.args))
end
-- automatic cleanup for count~=nil items (only when using run_check)
if data.count >= data.t.count then
print('autocleanup: '..tostring(key))
unregister(key)
end
return result
end
function run_check_all(...)
local n = select('#', ...)
for k,v in pairs(_registry) do
data = _registry[k]
if n>0 then
internal_run(data.t, ...)
else
internal_run(data.t, unpack(data.args))
end
end
end
-- mostly internal
function unregister(key)
_registry[key] = nil
print('key unregistered: '..tostring(key))
end
-- fn = the function to run (required)
-- count = how many times to run, nil for infinite (optional, default:nil)
-- start = the time to start runs (optional, default:nil)
-- interval = the seconds between runs (optional, default:nil)
-- key = key to use instead of fn (optional, default:fn)
function internal_run(t, ...)
local fn = t.fn
local key = t.key or fn
local now = os.clock()
local data = _registry[key]
if data == nil then
local args = {}
local n = select('#', ...)
local v
for i=1,n do
v = select(i, ...)
table.insert(args, v)
end
-- the first t and args are stored in registry
data = {count=0, last=0, t=t, args=args}
_registry[key] = data
end
--assert(data~=nil, 'data==nil')
--assert(data.count~=nil, 'data.count==nil')
--assert(now~=nil, 'now==nil')
--assert(data.t~=nil, 'data.t==nil')
--assert(data.t.start~=nil, 'data.t.start==nil')
--assert(data.last~=nil, 'data.last==nil')
-- run
local countCheck = (t.count==nil or data.count < t.count)
local startCheck = (data.t.start==nil or now >= data.t.start)
local intervalCheck = (t.interval==nil or now-data.last >= t.interval)
--print('', 'countCheck', tostring(countCheck))
--print('', 'startCheck', tostring(startCheck))
--print('', 'intervalCheck', tostring(intervalCheck))
--print('')
if countCheck and startCheck and intervalCheck then
data.count = data.count + 1
data.last = now
return fn(...) -- default to using latest args
end
end
-- TESTS -----------------------------------------------------------------------
local test=false
if test then
local function brutesleep(n)
local t0 = os.clock()
while os.clock() - t0 <= n do end
end
local all_tests = true
local start
if all_tests then
local f1 = function(...) print(...) end
for i=0,10 do
run_once(f1, "running once")
--brutesleep(0.2)
end
local f2 = function(...) print(...) end
for i=0,10 do
run_many(2, f2, "running two times", i)
--brutesleep(0.2)
end
local f3 = function(...) print(...) end
local start = os.clock()
while os.clock() - start < 4 do
--brutesleep(0.2)
run_every(1, f3, "running every second", os.clock())
end
local f4 = function(...) print(...) end
local f5 = function(...) print(...) end
start = os.clock()
print('function will run in 3 seconds...')
while os.clock() - start < 7 do
--brutesleep(0.2)
run_later(3, f4, "run once after 3 seconds")
run_every(1, f5, _registry[f4].t.start, os.clock())
end
local f4b = function(...) print(...) end
local f5b = function(...) print(...) end
start = os.clock()
local at = start+3
print('function will run @ '..tostring(at))
while os.clock() - start < 9 do
--brutesleep(0.2)
run_at(at, f4b, "run @ completed @ "..tostring(os.clock()))
run_every(1, f5b, _registry[f4b].t.start, os.clock())
end
end
if all_tests then
local f6 = function(...) print(...) end
start = os.clock()
run_later(1.5, f6, "run this in 1.5 seconds, but this arg isn't seen")
local i = 0
print('function will run when we check it late..')
while os.clock() - start <= 3 do
brutesleep(1)
i = i + 1
print(i)
end
run_later(42, f6, "finally checked, latest args are used, first param is not, it's meaningless")
end
-- to make this less confusing, i introduce run_check
if all_tests then
local f7 = function(...) print(...) end
start = os.clock()
run_later(1.5, f7, "run this in 1.5 seconds, once checked, these are original args")
local i = 0
print('-')
print('function will run when we check it late... using original args')
while os.clock() - start <= 2 do
brutesleep(1)
i = i + 1
print(i)
end
run_check(f7)
end
-- you can also use latest args with run_check
if all_tests then
local f8 = function(...) print(...) end
start = os.clock()
run_later(1.5, f8, "this arg wont matter")
local i = 0
print('-')
print('function will run when we check it late... using new args')
while os.clock() - start <= 2 do
brutesleep(1)
i = i + 1
print(i)
end
run_check(f8, "these are the new args")
end
-- test that cleanup does not occur with non check calls
local f9
if all_tests then
f9 = function(...) print(...) end
start = os.clock()
run_later(1.5, f9, "non-check cleanup check a")
local i = 0
print('-')
print('function will run when we check it late...')
while os.clock() - start <= 2 do
brutesleep(1)
i = i + 1
print(i)
end
run_later(1.5, f9, "non-check cleanup check b [GOOD]")
run_later(1.5, f9, "non-check cleanup check c")
end
if all_tests then
run_later(1.5, f9, "non-check cleanup check d") -- no error
end
-- cleanup occurs on check_calls where count~=nil
local f10
if all_tests then
f10 = function(...) print(...) end
start = os.clock()
run_later(1.5, f10, "check cleanup a")
local i = 0
print('-')
print('function will run when we check it late...')
while os.clock() - start <= 2 do
brutesleep(1)
i = i + 1
print(i)
end
run_later(1.5, f10, "check cleanup b [GOOD]")
run_later(1.5, f10, "check cleanup c")
end
if all_tests then
run_later(1.5, f10, "check cleanup d")
assert(_registry[f10] ~= nil)
run_check(f10, "check cleanup e")
if (_registry[f10] == nil) then
print('cleanup test passed')
end
end
-- test return values
if all_tests then
print('-')
local f11 = function(msg,x,y) print(msg); return x+y end
print("test return values in 2 seconds")
run_later(2, f11, 'function completed', 4, 9)
start = os.clock()
while os.clock() - start <= 2 do
brutesleep(0.2)
result = run_check(f11)
if result ~= nil then
print('result : '..tostring(result))
end
end
end
if all_tests then
print('- check all -')
run_check_all()
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment