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

Coroutine freezes the computer

Started by GamerNebulae, 06 June 2014 - 12:06 PM
GamerNebulae #1
Posted 06 June 2014 - 02:06 PM
Hello everyone!

I've been trying to wrap my head around the concept of coroutines (I was reading Bubba's tutorial about coroutines after I tried using the parallel API and it didn't work for some reason). I tried to create a coroutine and I got a report that the coroutine was suspended. All fine and dandy. But when I try to run the coroutines in a while-loop, they freeze up and I can't do anything. I'm using my own monitor API and try to use a function from there to listen if someone touches the monitor (nInteractive.mListen()) and I use a the function action() to make the computer do something. Does anyone know an answer? The code is down here:

Program I run on the computer: http://pastebin.com/FyCPmGqY
The API I use: http://pastebin.com/5LUUjVQ3
Edited on 06 June 2014 - 12:18 PM
Bubba #2
Posted 06 June 2014 - 02:17 PM
You're not passing anything back to the coroutines when you resume them so they don't receive any events. This isn't particularly important in the case of the action function, although it won't actually sleep it will just yield and then immediately be resumed. However, it does mean that you won't be catching any events from the os.pullEvent() on line 95 of the API.

Edit: I removed the code, because on second thought you ought to have a read-through of this section of my coroutines tutorial, which is much better than quickly scrapped together code.
Edited on 06 June 2014 - 12:25 PM
theoriginalbit #3
Posted 06 June 2014 - 02:22 PM
You're not passing anything back to the coroutines when you resume them so they don't receive any events.
Do you cover this in your tutorial (I've not actually read it)? out of the last 3 usages I've seen, no one seems to be passing the routines event data, so perhaps if you do cover it, make it more prominent, otherwise perhaps consider adding in a section to deal with this problem?
Bubba #4
Posted 06 June 2014 - 02:24 PM
You're not passing anything back to the coroutines when you resume them so they don't receive any events.
Do you cover this in your tutorial (I've not actually read it)? out of the last 3 usages I've seen, no one seems to be passing the routines event data, so perhaps if you do cover it, make it more prominent, otherwise perhaps consider adding in a section to deal with this problem?

I definitely do :)/>
theoriginalbit #5
Posted 06 June 2014 - 02:26 PM
hmmm, perhaps that section is a little ambiguous? maybe explicitly point out that its needed to do in order for the coroutine to get events? providing an event passing example too?
Edited on 06 June 2014 - 12:26 PM
Bubba #6
Posted 06 June 2014 - 02:27 PM
hmmm, perhaps that section is a little ambiguous? maybe explicitly point out that its needed to do in order for the coroutine to get events? providing an event passing example too?

I'm actually planning to rewrite that tutorial (and others) pretty soon. So yeah, things will hopefully be much clearer after the rewrite. It may also help that I intend for the tutorial to be in an interactive web environment… but more on that after I actually get things working properly :)/>
Edited on 06 June 2014 - 12:30 PM
GamerNebulae #7
Posted 06 June 2014 - 02:48 PM
-snip-

Bear with me here, I am a non-native English speaker. I tried reading the whole document again, which is almost the same as the post on CC forums, and I still don't get what I am doing wrong here.
Bubba #8
Posted 06 June 2014 - 03:19 PM
-snip-

Bear with me here, I am a non-native English speaker. I tried reading the whole document again, which is almost the same as the post on CC forums, and I still don't get what I am doing wrong here.

That's fine. I admit it probably sounds more confusing if you didn't write it. Here's a more basic overview without the complicated code:

Resuming a Coroutine - Arguments
What happens when you call coroutine.yield? In simple terms, the function (or block of code) currently running will stop until it can be resumed with coroutine.resume. But that's not the full story. coroutine.yield is quite handy in that you can get variables into and out of the running function simple by yielding. Let's look at an example:


local function resumeMe()
  print("Yielding now...")
  local a, b, c = coroutine.yield()
  print("The return values of coroutine.yield are: ")
  print(a .. "\t" .. b .."\t" .. c)
end

local co = coroutine.create(resumeMe)

coroutine.resume(co) --#We have to start our coroutine first - this will print "Yielding now..."
coroutine.resume(co, "arg1", "arg2", "arg3")
coroutine.status(co) --# The status is 'dead' because our function has finished

So what would you guess the output of this code would be? If you guessed "arg1 arg2 arg3", you'd be right. So what's going on here? Basically, anything extra arguments you pass through the coroutine.resume function will be returned by coroutine.yield. But what if you wanted to get output from a coroutine? How would you do that?

Well in fact it's pretty easy: We just add arguments to coroutine.yield():

local function resumeMe()
  print("Yielding now...")
  local a,b,c = coroutine.yield("something1", "something2", "something3")
  print("The return value of coroutine.yield is:")
  print(a .. "\t" .. b .. "\t" .. c)
end

local co = coroutine.create(resumeMe)
local a, b, c, d = coroutine.resume(co)
print("The coroutine yielded and returned these values: ")
print(a .. "\t" .. b .."\t" .. c .. "\t" .. d)
coroutine.resume("hi", "hello", "bye")

So what happens here? Well it might not be quite what you'd expect because calling coroutine.resume(co) is going to first return a boolean which indicates whether there were any errors or if the coroutine is dead. In the case, our output would be something like this:

Yielding now…
The coroutine yielded and returned these values:
true something1 something2 something3
The return value of coroutine.yield is:
hi hello bye

So, if we wanted to get that third argument of coroutine.yield we would need to add one more variable.

Now onto ComputerCraft and events
So you've probably been wondering: How does os.pullEvent() factor into this? What happens when we call os.pullEvent(). Well if you were to open up bios.lua, you might find something rather surprising:

function os.pullEventRaw( _sFilter )
return coroutine.yield( _sFilter )
end

That's right. os.pullEvent actually is just calling coroutine.yield. This means that you can use os.pullEvent exactly like you would a coroutine: let's give it a shot.


local function getEvent()
  local eventType, arg1, arg2 = os.pullEvent("key")
end

local co = coroutine.create(getEvent)
local status, eventFilter = coroutine.resume(co)

coroutine.resume(co, os.pullEvent(eventFilter))

And that's it!


I kind of wrote this in a rush, so apologies if there are mistakes or it's hard to understand.
Edited on 06 June 2014 - 01:36 PM
theoriginalbit #9
Posted 06 June 2014 - 03:23 PM
-snip-
just reading the code example's its better. though you made a mistake with coroutine.resume, it returns success before the arguments, see my examples here.
Edited by
Bubba #10
Posted 06 June 2014 - 03:27 PM
-snip-
just reading the code example's its better. though you made a mistake with coroutine.resume, it returns success before the arguments, see my examples here.

Thanks, fixed it. I'm brain-dead today. I even mentioned that it returned the success boolean first and then went ahead and ignored that in the output.
Edited on 06 June 2014 - 01:27 PM
theoriginalbit #11
Posted 06 June 2014 - 03:28 PM
Thanks, fixed it.
Missed one

local a, b, c = coroutine.resume(co)
GamerNebulae #12
Posted 06 June 2014 - 04:10 PM
-snip-

-snip-

I read everything and it cleared up the concept a little. But now the real question: how do I implement this in my code?
Geforce Fan #13
Posted 09 June 2014 - 05:06 AM
Basicly, what you're going to want to do is structure your program like this

function oneCoroutine()
  print("I'm a coroutine. I will only run two times though!")
  sleep(1) -- yields
  print"Well, this is the end of my life. Goodbye!"
  --now this coroutine will be dead, meaning it's useless.
end
function foreverCoroutine()
  while true do
	print"I will live forever but will yield after each run.
  end
end
function eventCoroutine
  local _,key=os.pullEvent("char")
  print(key)
end
--execute here
coOne = coroutine.create(oneCoroutine)
--we have created this coroutine but it will not run till we resume it
coTwo = coroutine.create(foreverCoroutine)
coThree = coroutine.create(eventCoroutine)
os.startTimer(0) -- this is so that it gets over the first pull event
--set the filter as a string for now.
filter = ""
while true do
  events = {os.pullEvent()}
  --since we know the first two coroutines have no event filters we
  --won't assign a variable to them, normally you would, but just
  --look at the last one to see how
  --the first one dies so we'll make sure we don't run it if it's dead
  if coroutine.status(coOne) == "suspended" then
	coroutine.resume(coOne)
  end
-- I won't redeminstrate here cause we know this will never die
  coroutine.resume(coTwo)
  if events[1] == filter then
	filter = coroutine.resume(coThree)
   end
end

that might have some errors but you get the idea. Of course you'll probably be doing all of what I'm doing in each 3 coroutine in 1 coroutine, but I've demonstrated it for each coroutine so you should be able to combine that into one.(if that last sentence makes no sense to you ignore it)
By the way, Bubba updated the coroutine tutorial, it's pretty good now.
Edited on 09 June 2014 - 03:12 AM