This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Cyclonit's profile picture

How to not miss any event?

Started by Cyclonit, 01 August 2012 - 03:18 PM
Cyclonit #1
Posted 01 August 2012 - 05:18 PM
Hi,

I'd like to write a small server function and actually have all of the saving, loading, sending functions etc. ready. The only problem there is, is that I do not understand how I can ensure that I do not miss any incoming message (aka any event at all).

My last idea was to use two functions called "collectMessages()" and "handleMessages()".
collectMessages() was supposed to loop indefinitely adding all of the occurring events to a global event queue which would then be emptied by handleMessages(). Since collectMessages() would've been running at any point of the program, functions running inside handleMessages() would not make me miss any events.

function collectMessages()

	local event, p1, p2, p3 = os.pullEvent()

	while (true) do
	
	    messageQueue = cf.list.pushBack(messageQueue, event)
	
		-- next event
		event, p1, p2, p3 = os.pullEvent()
	
	end
	
end

function handleMessages(currentQueue)

	if (currentQueue == nil) then
		return
	end

	local message = cf.list.popFront(currentQueue)
	
	while (message ~= nil) do
	
		-- do something here
	
	end

end

function serve()

	while (true) do
	
		currentQueue = messageQueue
		messageQueue = cf.list.new()
	
		parallel.waitForAny(collectMessages(), handleMessages(currentQueue))
		print("*")
	
	end

end

Sadly waitForAny does not break the loop I use in collectMessages() and thus this setup will never reach the point where it will handle the messages.

Any suggestions on how I can achieve my goal other than this?

Cyclonit
MysticT #2
Posted 01 August 2012 - 05:24 PM
A simple while loop using os.pullEvent should be enough:

local function handleEvent(evt, ...)
  if evt == "redstone" then
    -- do something here
  elseif evt == "rednet_message" then
    -- do something here
  elseif evt == "key" then
    -- do something here
  end
end

while true do
  handleEvent(os.pullEvent())
end
Just make sure to not use sleep, read, or anything that skips events inside the handle function.
Cyclonit #3
Posted 01 August 2012 - 05:27 PM
But that's exactly my issue. I have functions which must wait for responses from other computers etc. which have to skip events for them to reach the ones they are interested in. I know that two functions running in the parallel API have two independent event queues and thus I can screw with one of them as I like as long as I do not touch the other one.
MysticT #4
Posted 01 August 2012 - 05:41 PM
Well, they don't actually have different event queues, they just receive all the events from the same one.
You could make a function that receives the events, and calls the corresponding function in another coroutine. It would look something like this:

local tQueue = {}

local function queueFunction(func, ...)
  local co = coroutine.create(func)
  coroutine.resume(...)
  if coroutine.status(co) ~= "dead" then
    table.insert(tQueue, co)
  end
end

local function runCoroutines(...)
  for i, co in ipairs(tQueue) do
    coroutine.resume(co, ...)
    if coroutine.status(co) == "dead" then
	  table.remove(tQueue, i)
    end
  end
end

local function handleEvents()
  while true do
    local tEvt = { os.pullEvent() }
    if tEvt[1] == "rednet_message" then
	  if tEvt[2] == "some message" then
	    queueFunction(handleRednet, unpack(tEvt))
	  end
    end
    runCoroutines(unpack(tEvt))
  end
end
I'm not sure if that will work, but it will be something like that.

Or…
Spoileryou can use MysticOS and just create new threads for each function :ph34r:/>/>
Like this:

local function handleRednet()
  -- handle rednet messages here
end

while true do
  local tEvt = { os.pullEvent() }
  if tEvt[1] == "rednet_message" then
    os.createThread(os.runningProcess(), handleRednet)
  end
end
Cyclonit #5
Posted 01 August 2012 - 05:49 PM
The problem remains: I need to check events in several functions.

I'll simply write a new pullEvent function which adds the event to my own queue whenever I pull an event. I wont mess with the OS :ph34r:/>/>
MysticT #6
Posted 01 August 2012 - 06:40 PM
But the way I did it no events will be lost, since every function gets all the events.
It works like this:
It pulls an event.
Checks the event, and adds a new coroutine if needed.
Runs every coroutine, passing the event and it's arguments as parameters.

So, when the function in the coroutine calls coroutine.yield (called in os.pullEvent) it returns to the loop, wich runs the next coroutine until there's no more.

To understand a little better how coroutines work, try with these:

local function f1()
  while true do
    local evt = os.pullEvent()
    print("function 1: ", evt)
  end
end

local function f2()
  while true do
    local evt = os.pullEvent()
    print("function 2: ", evt)
  end
end

local co1 = coroutine.create(f1)
local co2 = coroutine.create(f2)
while true do
  local evt = os.pullEvent()
  print("Event: ", evt)
  coroutine.resume(co1, evt)
  coroutine.resume(co2, evt)
end
It should print something like:
Event: key
function1: key
function2: key
Event: char
function1: char
function2: char
maximal #7
Posted 11 December 2012 - 07:00 AM
I want he same thing and i have a little question about your implementation.

What will happen if a new event will be fired exactly after os.pullEvent returned another event before and before next line executed. Will that event be processed?
Lyqyd #8
Posted 11 December 2012 - 07:46 AM
Of course. All events that make it into the queue will eventually be pulled. The only thing that prevents events making it into the queue is a full queue. The queue is fairly large.

As a side note, I think you're overcomplicating this a great deal. A simple server script that keeps track of the status of each connection to it and acts appropriately upon receipt of each packet (regardless of order) is probably going to be a simpler option than spawning coroutines all over the place.