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

Queued event system?

Started by bbqroast, 07 July 2012 - 07:41 PM
bbqroast #1
Posted 07 July 2012 - 09:41 PM
So I was writing some software for a SMP "bank" using computer craft, but I hit a fundamental issue. If the server is busy when one of the clients sends a request then it will never even get the message. In order to respond to an event it must occur when the OS is actively listening for the event. Sadly this means implementing larger scale projects is very tricky, if not impossible.

So i suggest a new queued event system. When an event occurs it is added to the "queue", os.pullEvent() pulls the oldest event (or oldest event of the specified type). The event would be removed from the queue once it has been called.
Example:
Events that have happened:
Rednet message received (newest)
Player presses Q
Rednet message received
Disk inserted
Alarm set off (oldest)
The queue would look like this:
rednet_message,8,"blahaha"
char,"q"
rednet_message,2,"hai"
disk,3
alarm,3
Pseudo Code:

(vars) = os.pullEvent("rednet_message")
(vars) = os.pullEvent()
Resulting queue
rednet_message,8,"blahaha"
char,"q"
disk,3
The first one would return the second rednet message ("hai" from ID 2) and the second command would return the alarm. Note something here, even though the rednet message "hai" was called (being the oldest rednet message) the computer still returned to the start of the list, so the next (unlimited) call returned the alarm (as such a good program should cycle through all the events ignoring the ones it doesn't want. Using the selective feature always would cause a giant queue to build up).

EDIT:
People seem sure this is implemented, BUT:
It doesn't seem to be working with rednet.
On the first computer I opened the LUA console, booted rednet and prepared the command:
rednet.broadcast("Oh hai!")
On the second I wrote the following script
rednet.open("left")
sleep(8)
local i, i2 = os.pullEvent()
print(i..i2)
Then I ran the script and quickly ran the command on the first computer, the second computer slept for a while and then waited for a command- no luck.
Does this work with rednet?
EDIT2: I did the same test, hitting "space" after I ran the script, it did not pick up the space (which should have been queued, after the computer stopped sleeping and moved onto the os.pullEvent() command I was able to hit it to end the program).
libraryaddict #2
Posted 07 July 2012 - 10:00 PM
Sadly this means my while true do loops will begin to make the server lag..
bbqroast #3
Posted 07 July 2012 - 10:08 PM
Sadly this means my while true do loops will begin to make the server lag..
How exactly?
Once all the queued events have been cleared it will just wait until a new one pops up (like it does now). Besides infinite loops are sloppy programming, you should have a tick/second limiter.
Pinkishu #4
Posted 07 July 2012 - 10:15 PM
I don't really get what you mean
when is the os "busy"?
MysticT #5
Posted 07 July 2012 - 10:20 PM
You know that this is what actually happens right? There's an event queue, in wich the events are queued, and os.pullEvent (or coroutine.yield) gets the oldest.
That's why there's a method called os.queueEvent.
Cloudy #6
Posted 07 July 2012 - 10:22 PM
Except this is exactly how it already works. Events ARE queued until they are pulled off the stack. Perhaps you are calling another command which disregards any events that are received which aren't the event they are looking for.

Edit: Ninja's are everywhere these days!
bbqroast #7
Posted 07 July 2012 - 10:24 PM
I don't really get what you mean
when is the os "busy"?
As in doing something other than listening for redstone messages. Say you have a relay like this:
(Pseudo code)

while true do
a = listen for rednet message
broadcast a over rednet
end
This code will listen for rednet messages, and then broadcast them. Using a chain of computers you can send messages a long way.
However, what if you built this in SMP with lots of people on? Player a might send a message, and then Player B might send one a fraction of a second later, the computer will be busy broadcasting A's message, and B's will go unnoticed.
You know that this is what actually happens right? There's an event queue, in wich the events are queued, and os.pullEvent (or coroutine.yield) gets the oldest.
That's why there's a method called os.queueEvent.
I thought it might, but I did some testing and it doesn't seem to work.
Except this is exactly how it already works. Events ARE queued until they are pulled off the stack. Perhaps you are calling another command which disregards any events that are received which aren't the event they are looking for.

Edit: Ninja's are everywhere these days!
Just using the standard functions… I will try a bit more :)/>/>.
yeah, you and the guy above replied as I replied to the guy above the guy above you.

Ok, it doesn't seem to be working with rednet.
On the first computer I opened the LUA console, booted rednet and prepared the command:
rednet.broadcast("Oh hai!")
On the second I wrote the following script
rednet.open("left")
sleep(8)
local i, i2 = os.pullEvent()
print(i..i2)
Then I ran the script and quickly ran the command on the first computer, the second computer slept for a while and then waited for a command- no luck.
Does this work with rednet?
Cloudy #8
Posted 07 July 2012 - 10:26 PM
I can 100% confirm that this is exactly what happens - mind posting the problem code?
bbqroast #9
Posted 07 July 2012 - 10:34 PM
I can 100% confirm that this is exactly what happens - mind posting the problem code?

rednet.open("left")
sleep(8)
local i, i2 = os.pullEvent()
print(i..i2)
Or does it not listen when sleeping?
MysticT #10
Posted 07 July 2012 - 10:36 PM
Why don't you take a look at the sleep code? It pulls all the events from the queue, that's why you don't get them.
All the turtle and http.get and http.post functions also do this.
bbqroast #11
Posted 07 July 2012 - 10:41 PM
What (exactly) is the point of doing that? It completely ruins the idea of sleeping to prevent server lag…
Pinkishu #12
Posted 07 July 2012 - 10:44 PM
What (exactly) is the point of doing that? It completely ruins the idea of sleeping to prevent server lag…

Uhm well it makes a timer event and pulls events till it gets the timer event it waits for and until then it does nothing much and if theres no event to pull it just idles till theres one
so i don't see how that goes against preventing server lag
MysticT #13
Posted 07 July 2012 - 10:47 PM
And why would something like:

local id, msg = rednet.receive()
if id == someID then
  if msg == correctMessage then
	doSomething()
  end
end
cause server lag? it's does nothing until it gets a message.
Each time you yield, the computer is inactive, it does nothing until there's an event.
bbqroast #14
Posted 08 July 2012 - 12:04 AM
And why would something like:

local id, msg = rednet.receive()
if id == someID then
  if msg == correctMessage then
	doSomething()
  end
end
cause server lag? it's does nothing until it gets a message.
Each time you yield, the computer is inactive, it does nothing until there's an event.
My point is, most programs run in infinite loops (which are ended when the user quits, for example a game might do -> Physics run, Object logic, AI logic, render). When I'm programming C++ I always set up a system to maintain a maximum "tick rate", normally 40ticks/second. Otherwise the loop will run as fast is it can putting a lot of stress on the CPU. I do this by dividing 1000/40 and then seeing if the tick was quicker than the result (milliseconds), if so I "sleep" until the tick time = 1000/40 ms. That way I always get 40 ticks in a second (unless my computer is lagging, in which case it will be less) which prevents my CPU from heating up (laptop). Looking at the way CC works I don't feel this is needed :)/>/>.
glopso #15
Posted 12 July 2012 - 03:41 PM
You could design your own sleep function that doesn't dump the queue. That might end up causing its own lag though, since what I have in mind is just a while loop checking if the time is past a certain point.
kazagistar #16
Posted 12 July 2012 - 06:39 PM
This is almost certainly overkill for your use, but this is trivial to do using core, the nice multiprocessing/event multiplexing center of my in-development OS. (I am about 90% sure the current version of core works, but as it is in development there is a chance it is a broken build, and I will almost certainly modify it in the future.)

Basically, I let you set "event listener functions". Whenever an event is triggered, the event listener is called.
(1) You can have multiple event listeners listen for the same event.
(2) You can have one event listener listen for multiple events.
(3) The event listener can chose to unregister itself or not after it is called.
(4) A program can register and unregister event listeners at will.
(5) There is a "threading" system that lets you run multiple, normal programs, each of which can use os.pullEvent() freely, and each thread will receive every event, similar to how the parallel api works.

The linked file is actually 3 different libraries glued together… There is an event reactor library, a threading library which depends heavily on the event reactor, and a (as of yet unmentioned) persistence library which is separate from the other two. Feel free to delete unwanted parts, it should still work, and if you try it, send me any bugs you encounter. I tried to include a reasonably comprehensive usage guide in the comments at the beginning.