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

Multitasking (Coroutines)

Started by Exerro, 19 August 2013 - 06:17 AM
Exerro #1
Posted 19 August 2013 - 08:17 AM
Multitasking is a heavily used thing in most modern programs. For example windows, multitasking allows Windows to have more than one different program running at once. Even on these forums, a lot of the newer OSs include some sort of multitasking or use of coroutines. Even commonly used things like os.pullEvent are related to coroutines. If you don't know: os.pullEventRaw( ) just returns coroutine.yield( ) and os.pullEvent( ) uses os.pullEventRaw( ).

In principle, they are quite easy to understand, but using them correctly is slightly harder.
A coroutine isn't exactly multitasking, but it is the closest thing to it in CC. All it is, is a function that stops running when coroutine.yield( ) is called from inside the function, and it can be resumed from the point it stopped.

How to create a coroutine:
 my_coroutine = coroutine.create( my_function ) 
Simple eh?

Ok, so what do you do with this? To run it you use:
 ok, err = coroutine.resume( my_coroutine, ... ) 
coroutine.resume( ) returns whether the coroutine ran without erroring, and if it did error, it also returns the error message

We are nearly there! Now it is more about learning what you have to do with the function you want to use as a coroutine…
coroutine.yield( ) returns the arguments passed to it from coroutine.resume( ), so generally, people resume coroutines with events. A quick demo of a coroutine setup:

local function my_function( ) -- this is just a demo function
	while true do
		local event, key = coroutine.yield( ) -- this stops the coroutine and waits for it to be resumed. It will set event, key to the arguments that it is resumed with
		if event == "key" then
			print( key )
		else
			error( "You didn't press a key!" )
		end
	end
end
local function my_other_function( )
	while true do
		sleep( 10000 ) -- would normally make the other function wait 10000 seconds
	end
end
local my_coroutine = coroutine.create( my_function ) -- creates a coroutine from my_function
local my_other_coroutine = coroutine.create( my_other_function ) -- ditto
while true do -- an infinite loop
	local event = { os.pullEvent( ) } -- captures the event in a table
	local ok, err = coroutine.resume( my_coroutine, unpack( event ) ) -- runs the coroutine with the event like [event, key, etc]
	coroutine.resume( my_other_function, unpack( event ) ) -- runs this too, and when sleep is called, it stops running because sleep->os.pullEvent->os.pullEventRaw->coroutine.yield( )
	if not ok then -- if the coroutine errored then...
		print( err ) -- print the error
		break -- exit from the loop
	end
end

If a coroutine errors, you will no longer be able to resume it. You can check if a coroutine is ready to be resumed using the command coroutine.status( my_coroutine )
This will return the status of the coroutine
"dead" means you cannot resume it, and if you try, it will return [false, "cannot resume dead coroutine"]
"suspended" means it is ready to be resumed

I hope this tutorial has helped with understanding coroutines. Like all things, "practise makes perfect", so get on computercraft and start messing around with coroutines
Thanks for reading
Last1Here #2
Posted 19 August 2013 - 08:22 AM
very interesting and informative, thank you
Zudo #3
Posted 19 August 2013 - 01:50 PM
Nice :)/>
PixelToast #4
Posted 19 August 2013 - 05:01 PM
not really a proper way of resuming, you need to implement filtering and terminate (though os.pullEvent will do that)
immibis #5
Posted 15 September 2013 - 08:04 PM
You run the same coroutine twice, and don't ignore "char" events.