Skip to content

Instantly share code, notes, and snippets.

@prozacgod
Last active May 30, 2017 16:02
Show Gist options
  • Save prozacgod/ee9f67a1b8f9be4369c6d1cda7fe081e to your computer and use it in GitHub Desktop.
Save prozacgod/ee9f67a1b8f9be4369c6d1cda7fe081e to your computer and use it in GitHub Desktop.
OpenComputers signals low level pull signal, with future schedules pseudo signals, and a threadlet kernel.
local event = require('event')
local uptime = require('computer').uptime
local signals = {
_pullSignal = function(timeout)
return event.pull(timeout)
end
}
local p_events = {}
local p_handle = 1
function getNextPEvent()
local selected_event = { time = math.huge }
for handle, p_event in pairs(p_events) do
if p_event.time < selected_event.time then
selected_event = p_event
end
end
if selected_event.handle == nil then
return nil
end
return selected_event
end
signals.schedule = function(time, event_data)
local handle = p_handle
p_handle = p_handle + 1
p_events[handle] = {
time = uptime() + time,
handle = handle,
event_data = event_data
}
return handle
end
signals.cancel = function(handle)
p_events[handle] = nil
end
signals.pull = function(userTimeout)
userTimeout = userTimeout or math.huge
local current_time = uptime()
local next_pevent = getNextPEvent()
local pevent_data = nil
local timeout = userTimeout
-- we have a scheduled pseudo-signal
if next_pevent then
local p_timeout = next_pevent.time - current_time
if (userTimeout >= p_timeout) then
pevent_data = next_pevent.event_data
timeout = p_timeout
end
-- this signal is overdue, return it now!
if p_timeout <= 0 then
signals.cancel(next_pevent.handle)
return pevent_data
end
end
-- next pseudo-signal is in the future, lets see if new events come from the hardware first
local data = {signals._pullSignal(timeout)}
if data[1] == nil then
if pevent_data then
signals.cancel(next_pevent.handle)
end
return pevent_data
end
return data
end
return signals
package.loaded.threadlet = nil
package.loaded.signals = nil
local threadlet = require("threadlet")
-- this function will exit when 'q' is pressed
local function keyboardExitThreadlet()
print ("starting thread!, press q for this thread to die")
while true do
local evt, addr, a, b, c, d, e, f = threadlet.pullEvent('key_up')
-- q pressed filter, lets exit!
if evt == "key_up" and a == 113 then
break
end
end
end
local function eventSpyThreadlet()
local w,h = term.getViewport()
while true do
local evt = {threadlet.pullEvent()}
local evtStr = ""
for i,v in ipairs(evt) do
evtStr = evtStr .. tostring(v) .. " "
end
term.setCursor(1, h)
term.write("EVT: " .. evtStr .. " ")
end
end
local function sleepTimer1()
while true do
print(" sleeping! TIMER 1")
threadlet.sleep(1)
print(" Sleep timer 1")
end
end
local function sleepTimer2()
while true do
print("------- sleeping! TIMER 2")
threadlet.sleep(3)
print("------- Sleep timer 2")
end
end
local function sleepTimeoutExit()
print("Sleeping! press q before timeout to exit")
threadlet.sleep(12)
print("Timeout over, exiting!")
end
threadlet.kernel(keyboardExitThreadlet, sleepTimer1, sleepTimer2, sleepTimeoutExit)
local signals = require("signals")
local threadlet = {1,2,3}
threadlet.pullEvent = function(...)
return coroutine.yield(...)
end
local sleep_handle = 1
threadlet.sleep = function(timeout)
local handle = sleep_handle
sleep_handle = sleep_handle + 1
signals.schedule(timeout, {'timer', 'self', handle})
while true do
local evt, addr, h = threadlet.pullEvent('timer')
if handle == h then
break
end
end
end
threadlet.kernel = function(...)
local threadlets = {}
local threadletFuncs = {...}
function threadletHandlerFactory(func)
local co = coroutine.create(func)
local evt_filter_name = nil
return {
co = co,
resume = function(evt_data)
local evt_name = nil
if evt_data ~= nil then
evt_name = evt_data[1]
end
if evt_filter_name ~= nil and evt_name ~= evt_filter_name then
return
end
if evt_data == nil then
evt_data = {}
end
local noErr, evt_filter = coroutine.resume(co, table.unpack(evt_data))
evt_filter_name = evt_filter
end
}
end
for i, func in ipairs(threadletFuncs) do
threadlets[i] = threadletHandlerFactory(func)
end
local threadCount = #threadlets
local evt_data
while true do
for i = 1, #threadlets do
threadlets[i].resume(evt_data)
end
for i = #threadlets, 1, -1 do
if coroutine.status(threadlets[i].co) == 'dead' then
table.remove(threadlets, i)
end
end
if #threadlets < threadCount then
break
end
evt_data = signals.pull()
end
end
return threadlet
package.loaded.threadlet = nil
package.loaded.signals = nil
local threadlet = require("threadlet")
-- this function will exit when 'q' is pressed
local function keyboardExitThreadlet()
print ("starting thread!, press q for this thread to die")
while true do
local evt, addr, a, b, c, d, e, f = threadlet.pullEvent('key_up')
-- q pressed filter, lets exit!
if evt == "key_up" and a == 113 then
break
end
end
end
local function eventSpyThreadlet()
local w,h = term.getViewport()
while true do
local evt = {threadlet.pullEvent()}
local evtStr = ""
for i,v in ipairs(evt) do
evtStr = evtStr .. tostring(v) .. " "
end
term.setCursor(1, h)
term.write("EVT: " .. evtStr .. " ")
end
end
local function sleepTimeout()
print("Sleeping! press q before timeout to exit")
threadlet.sleep(2)
print("Timeout over, exiting!")
end
threadlet.kernel(keyboardExitThreadlet, sleepTimeout)
@prozacgod
Copy link
Author

Fixed another issue, overdue events were getting lost to the ether, if a lot of events (drag events) came in some pseudo-signals were failing to fire, leading to inconsistent events.

This fix makes it so that it should run exactly the same number of events, obviously timing isn't precise due to the nature of OC and the environment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment