I have to ask and this is a very vague question,
How do you implement multitasking into an OS? I have tried many times, but can't seem to get my head around how it would work,
Any help would be awesome thanks!
- Danny -
You'd create a coroutine manager. Programs would be started as coroutines and the distribution of events to them is handled by your coroutine manager. You only give the player input events to whatever coroutine is currently in focus. If you have questions about specific parts of the general structure, feel free to ask!
1. Read the link Lignum posted. It is a very good tutorial on how coroutines work
2. You don't "redraw" the coroutine. The coroutine will draw itself, but your manager will probably assign a window to each routine, which will be redirected to while that routine is running.
3. Yes, buffers are helpful.
4. Simple Explanation of coroutines:
You have a program. The program runs another program until that program yields, and saves the result to use as a filter for events.
This is already using a coroutine, however it is not using it in any useful way. Now imagine we have two coroutines. Coroutine A and Coroutine B. You first run A until it yields, then run B until it yields, and repeat. If a coroutine's status becomes "dead" at any point, you'll want to remove it.
Really, just read the tutorial. It'll do a much better job explaining than me.
Your coroutine will boil down to a function that you're running alongside other functions. Whenever it yields, you might grant some other coroutines some run-time before you allow it to resume.
Whether or not that coroutine draws anything entirely depends on the function you built it out of. Resuming a coroutine doesn't automatically redraw everything that function had drawn in the past. The functions for dealing with coroutines have absolutely no control over your display.
If you want to run multiple scripts at the same time via coroutines, and are worried that they might draw over each other's output, that's when you'd rig up a display buffering scheme - redirect each script to a separate buffer object, then manually trigger a redraw of that before resuming the associated coroutine. This is exactly how multishell operates, using the window API to handle its display buffering.
local task1 = coroutine.create(afunction())
local task2 = coroutine.create(anotherf())
table.insert(taskstable, task1)
table.insert(taskstable, task2)
coroutine.resume(taskstable[1])
local task1 = coroutine.create(afunction()) local task2 = coroutine.create(anotherf()) table.insert(taskstable, task1) table.insert(taskstable, task2)
Also how would I open a file and run the contents of that program in a coroutine?
local task1 = coroutine.create(afunction()) local task2 = coroutine.create(anotherf()) table.insert(taskstable, task1) table.insert(taskstable, task2)
This would more of less work, but when creating your coroutines you need to build them out of functions. "afunction()" runs a function and returns the result, and you can't build a coroutine out of that result (unless that also happens to be a function) - you'd generally just pass in "afunction" directly.Also how would I open a file and run the contents of that program in a coroutine?
Look into the "loadfile" function.
--Test:
function maths()
while true do
print("> Maths stuff")
print("Press 1 to continue")
print("Press 2 to go back")
local args = { os.pullEvent("char") }
if args[2] == "1" then
for i = 1, 10 do
print(i)
end
elseif args[2] == "2" then
coroutine.yield()
end
end
end
function calc()
while true do
print("> Calculator")
print("Press 1 to continue")
print("Press 2 to go back")
local args = { os.pullEvent("char") }
if args[2] == "1" then
print("> 1st number:")
local nNum1 = tonumber(read())
print("> 2nd number:")
local nNum2 = tonumber(read())
print(nNum1.." + "..nNum2.." = "..nNum1+nNum2)
elseif args[2] == "2" then
coroutine.yield()
end
end
end
function main()
while true do
print("Press the numerical number for either option")
print("1. Maths")
print("2. Calculator")
local args = { os.pullEvent("char") }
if args[1] == "char" then
if args[2] == "1" then
coroutine.resume(mathsco)
elseif args[2] == "2" then
coroutine.resume(calcco)
end
end
end
end
parent = coroutine.create(main())
mathsco = coroutine.create(maths())
calcco = coroutine.create(calc())
main()
function os.pullEventRaw( sFilter )
return coroutine.yield( sFilter )
end
function os.pullEvent( sFilter )
local eventData = { os.pullEventRaw( sFilter ) }
if eventData[1] == "terminate" then
error( "Terminated", 0 )
end
return unpack( eventData )
end
Bear in mind that os.pullEvent() is basically a fancy version of coroutine.yield(). The main difference is that it's rigged to pre-emptively catch terminate events, and handles them in a special way. Here's the relevant code from bios.lua:function os.pullEventRaw( sFilter ) return coroutine.yield( sFilter ) end function os.pullEvent( sFilter ) local eventData = { os.pullEventRaw( sFilter ) } if eventData[1] == "terminate" then error( "Terminated", 0 ) end return unpack( eventData ) end
mathsco = coroutine.create(maths())
mathsco = coroutine.create(maths)
coroutine.yield()
local args = { os.pullEvent("char") }
One reason is that you're still attempting to build coroutines out of function results instead of functions, which as I mentioned, won't work. To be more specific, that means you need to change this sort of thing:mathsco = coroutine.create(maths())
… to this sort of thing:mathsco = coroutine.create(maths)
You also seem to be thinking that this:coroutine.yield()
… will break out of your functions in a way that this:local args = { os.pullEvent("char") }
… won't. However, both coroutine.yield() and os.pullEvent() will yield, because os.pullEvent() calls coroutine.yield() - there's very little difference between the two.
And then there's the matter of passing event data back into your coroutines when you resume them, and figuring out when it's time not to resume them…
Long story short, you need to further your understanding of how coroutines work before you can start writing this sort of code. Your current attempt can't simply be fixed by altering a couple of lines.
I suggest going back to the tutorial Lignum linked you to, and asking questions about any specific parts you don't understand. Once you feel you're familiar with the subject material, read the source code of the parallel API until that makes sense to you too. If you want an example of how you might further build that sort of logic into an OS, consider reading the source of the multishell script, too. From there, you'll have a much better foundation of knowledge from which to start writing your own code.
The multishell script is the "menu" you're asking for. Each script gets a tab. You click a tab, the relevant script "takes focus", so to speak. Scripts which aren't "in focus" are not resumed with "user input" events such as mouse clicks or typed characters. Window objects are used to handle the conflicts in screen usage.
But read the parallel API first. It's a far simpler coroutine manager which takes a list of functions, rigs them up as coroutines and resumes each in turn over and over again until either one dies (when calling parallel.waitForAny), or until they all die (when running parallel.waitForAll). Take note of the way in which it uses the "filters" to work out which events should be used to resume which coroutines, the way in which it passes that event data on to resumed coroutines, and the way it handles dead coroutines.
If possible could you help me with this, code would be helpful so I can see how it done, so I can implement it myself because at the moment, I don't know where to start and when i say I am struggling to get my head around it you say read another link and this link still doesn't help, if possible I need examples, thanks again though as this is a big help!
If possible could you help me with this, code would be helpful so I can see how it done, so I can implement it myself because at the moment, I don't know where to start and when i say I am struggling to get my head around it you say read another link and this link still doesn't help, if possible I need examples, thanks again though as this is a big help!
Just saying "you don't get it" doesn't get us anywhere. Go back to the tutorial, and if you encounter a part you don't understand, point out the segment and I can rephrase it for you.
Likewise, the parallel API is a perfectly good code example. Don't understand what a given line is doing? Then quote out the first line that's giving you trouble, and it can be explained to you - etc until you get it.