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

Coroutine Uses?

Started by MxHn, 13 August 2013 - 04:09 PM
MxHn #1
Posted 13 August 2013 - 06:09 PM
I understand that coroutines can be useful for pausing programs neatly and accepting more augments. I can't seem to see any other uses. I also do not get multitasking. Tried to crack the official manual and tutorials but can not seem to get this. Can someone point to a good book or tutorial to help me?
electrodude512 #2
Posted 13 August 2013 - 06:18 PM
example: has 2 threads - one serves rednet requests and the other does user interface stuff

function server()
    while true do
        event={os.pullEvent()}
        if event[1]=="rednet_message" then
            print("Got rednet message from "..event[2]..", "..event[4].."blocks away, contents as follows:"..event[3])
        end
    end
end

function ui()
    io.write("Enter number: ")
    num=tonumber(read())
    print("You entered "..num)
end

parallel.waitForAny(server, ui)

The parallel API uses coroutines to run a bit of one thread, then a bit of the other, switching threads every os.pullEvent() or coroutine.yield(). BTW, both of theses functions do practically the same thing.
MxHn #3
Posted 13 August 2013 - 06:22 PM
True but I am curious what hppends in the background of the parallel functions. Read the code but can't make sense of it. I guess the problem stems from the fact I do not understand the coroutine functions. Corutine.resume waits until yeald right?
Bubba #4
Posted 13 August 2013 - 06:34 PM
Hi MxHn,

I've written up a tutorial on coroutines which may be useful to you. You can find it here. If you have any more questions on coroutines after you've read it, I'd be happy to answer them.
theoriginalbit #5
Posted 13 August 2013 - 09:33 PM
-snip- os.pullEvent() or coroutine.yield(). BTW, both of theses functions do practically the same thing.
Not practically, they do, do the same thing. os.pullEvent calls os.pullEventRaw, which in turn calls coroutine.yield…
MxHn #6
Posted 13 August 2013 - 11:17 PM
What does this line do?
(age<5 and “older” or “younger”)
immibis #7
Posted 14 August 2013 - 03:59 AM

a = (age < 5 and "older" or "younger")
is like

if age < 5 then
  a = "older"
else
  a = "younger"
end

It's a space-saving trick that doesn't really belong in a tutorial.
Bubba #8
Posted 14 August 2013 - 06:25 AM
It's a space-saving trick that doesn't really belong in a tutorial.

Probably. However, it is supposed to be a rather advanced tutorial. Anyway, I'll look into removing that sometime.
MxHn #9
Posted 14 August 2013 - 09:37 PM
Still useful if given a correct explanation.
MxHn #10
Posted 15 August 2013 - 01:25 PM
Does coroutine.resume waite for the coroutine to complete? If so then why does this code work?
 local getC = coroutine.create(getChars)
local saveC = coroutine.create(saveChars)

local evt = {}

while true do
	coroutine.resume(getC, unpack(evt))-- why supply agument if getC is not defined to accept augments?
	coroutine.resume(saveC, unpack(evt) )-- execution will not get here if coroutine.Resume waits for yeild before going on because getC has a while true loop
	if coroutine.status(getC) == "dead" then
		break
	end
	evt = {os.pullEvent()}
end
theoriginalbit #11
Posted 15 August 2013 - 01:54 PM
Does coroutine.resume waite for the coroutine to complete?
No so much complete, more yield. The coroutine will run until 1 of 2 things happen:

1. It yields. i.e. coroutine.yield has been called (lots of functions call this, e.g. os.pullEvent, os.pullEventRaw, sleep, any turtle function, red net.receive, any http function… basically anything that is waiting for an event)
2. It has run too long and is killed by the VM with a "too long without yielding" error.

So basically that code works because first the getC coroutine is resumed with the event details, it processes them and then yields (the os.pullEvent). Then the saveC coroutine is resumed with the event details, it processes them and then again yields (again with the os.pullEvent). And this just continues in a loop until the enter key is pressed. Once pressed the getC is resumed, it processes and breaks from it's loop, it will now be complete or "dead". The saveC coroutine is then run, it processes the event for enter, saves the file and yields once more. The main loop then detects that the getC coroutine is dead and no longer runs the program. This leaves the saveC coroutine as paused for a little while until LuaJ decides to clean it up (once it is no longer referenced of course, normally via going out of scope) along with the resources used for getC.
MxHn #12
Posted 15 August 2013 - 02:00 PM
Ah I kind of see. If it yeilds at the place why does it get to the processing? And where does the unpacked event go?
theoriginalbit #13
Posted 15 August 2013 - 02:24 PM
ok so to start, imagine we didn't actually put the events in a table, imagine we did this instead


local function routineOne()
  while true do
	local event, param1, param2, param3 = coroutine.yield() --# can also be os.pullEvent, or os.pullEventRaw
	if event == "abc" then
	  print( param1 ) --# prints 'foo' each time
	  print( param2 ) --# prints 'bar' each time
	  print( tostring(param3) ) --# prints 'nil' each time as it never gets a 3rd parameter
	end
  end
end

local one = coroutine.create(routineOne)

while true do
  coroutine.resume( one, "abc", "foo", "bar" ) --# here we give the coroutine "one" the arguments of "abc", "foo" and "bar" for it to do appropriately with.
end

now obviously the unpacking of the table works in the same way, by supplying all the event data to the coroutine through the facility of the coroutine.yield's return values…

It should also be noted that the coroutine.yield can communicate back with the resumer by supplying parameters (just like any function call)

using my above example I will show this, I will have the coroutine.yield request that it only be sent the "abc" event, however the resumer will just ignore it and instead print it's request.


local function routineOne()
  while true do
	local event, param1, param2, param3 = coroutine.yield("abc") --# the resumer will now get "abc" as a return value from this coroutine
	if event == "abc" then
	  print( param1 ) --# prints 'foo' each time
	  print( param2 ) --# prints 'bar' each time
	  print( tostring(param3) ) --# prints 'nil' each time as it never gets a 3rd parameter
	end
  end
end

local one = coroutine.create(routineOne)

while true do
  local ok, val1, val2 = coroutine.resume( one, "abc", "foo", "bar" ) --# this is setup to see if the coroutine resumed ok and then to get 2 return values, it will however only get 1
  print(ok) --# this will say "true"
  print(val1) --# this will say "abc"
  print(tostring(val2)) --# this will say "nil"
end
MxHn #14
Posted 15 August 2013 - 02:41 PM
Thanks Bit!