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

Multitasking doesn't work the way I expected?

Started by DannySMc, 24 June 2015 - 09:37 AM
DannySMc #1
Posted 24 June 2015 - 11:37 AM
So I made this:
--[[
	tRunning = {{name}, {window}, {thread}}		
]]

local thread = {
	tRunning = {{},{},{}},
	create = function( self, name, func )
		table.insert(tRunning[1], name)
		table.insert(tRunning[2], window.create(term.current(), 1, 1, 51, 19, false))
		if func then
			table.insert(tRunning[3], coroutine.create(func))
		else
			table.insert(tRunning[3], false)
		end
		return true
	end,
	resume = function( self, name )
		for k, v in ipairs(tRunning[1]) do
			if v == name then
				if sCurrent then
					for k1, v1 in ipairs(tRunning[1]) do
						if v1 == sCurrent then
							tRunning[2][k1].setVisible(false)
						end
					end
				end
				sCurrent = name
				tRunning[2][k].setVisible(true)
				term.redirect(tRunning[2][k])
				if tRunning[3][k] then
					coroutine.resume(tRunning[3][k])
				end
			end
		end
	end,
	list = function( self )
		return tRunning
	end,
	kill = function( self, name )
		for k,v in ipairs(tRunning[1]) do
			if k == name then
				table.remove(tRunning[1][k])
				table.remove(tRunning[2][k])
				table.remove(tRunning[3][k])
			end
		end
	end,
	status = function( self, name )
		for k,v in ipairs(tRunning[1]) do
			if k == name then
				
			end
		end
	end,
	run = function( self )
	end,
}

and I want it so I can do: thread:create(function)

then that adds that to the list of functions, then I can do like: thread:run() and it will run them all, now this is the part I am not sure on, how do I switch to an app? and then direct all the arguments that are given to that?

So I want to be able to make a function that holds the information for each screen and all the content so say (my app store) the menu function is here:

functiom menu()
  -- menu code
end

and I have the upload app function:
function upload()
  -- do upload function here
end

Every function has a os.pullEvent() that allows you to interact with the screen so how could I use the above to switch between them? so the code above (top) comes with a name, window and coroutine, so how would I go about making it so when I run a new task it creates the coroutine and runs it, loading all the screen information etc, then when I click on say the top menu bar to go to another app it then will thread:resume("menu") and then load the window for that and then run that, still keeping in mind that the os.pullEvent() has to work, so I catch the pullEvent events and send them back to the running process so I can then use them to do what I need?


Spoiler
-- Main Functions
omi = {}
omi.__index = omi

-- Draw Functions
omi.draw = {}
omi.draw.__index = omi.draw

-- Database (CCSystems) Functions
omi.ccsys = {}
omi.ccsys.__index = omi.ccsys

term.clear()
term.setCursorPos(1, 1)

--[[
	tRunning = {{name}, {window}, {thread}}		
]]

local thread = {
	tRunning = {{},{},{}},
	create = function( self, name, func )
		table.insert(tRunning[1], name)
		table.insert(tRunning[2], window.create(term.current(), 1, 1, 51, 19, false))
		if func then
			table.insert(tRunning[3], coroutine.create(func))
		else
			table.insert(tRunning[3], false)
		end
		return true
	end,
	resume = function( self, name )
		for k, v in ipairs(tRunning[1]) do
			if v == name then
				if sCurrent then
					for k1, v1 in ipairs(tRunning[1]) do
						if v1 == sCurrent then
							tRunning[2][k1].setVisible(false)
						end
					end
				end
				sCurrent = name
				tRunning[2][k].setVisible(true)
				term.redirect(tRunning[2][k])
				if tRunning[3][k] then
					coroutine.resume(tRunning[3][k])
				end
			end
		end
	end,
	list = function( self )
		return tRunning
	end,
	kill = function( self, name )
		for k,v in ipairs(tRunning[1]) do
			if k == name then
				table.remove(tRunning[1][k])
				table.remove(tRunning[2][k])
				table.remove(tRunning[3][k])
			end
		end
	end,
	status = function( self, name )
		for k,v in ipairs(tRunning[1]) do
			if k == name then
				
			end
		end
	end,
	run = function( self )
	end,
}

function omi.init()
    local ok, err = pcall( function ()
    	if http then
    		if fs.exists("progutils") then
    		os.loadAPI("progutils")
    		    return true
    		end
    		aa = aa or {}
    		local a = http.get("https://vault.dannysmc.com/lua/api/dannysmcapi.lua")
    		a = a.readAll()
    		local env = {}
    		a = loadstring(a)
    		local env = getfenv()
    		setfenv(a,env)
    		local status, err = pcall(a, unpack(aa))
    		if (not status) and err then
    		    printError("Error loading api")
    		    return false
    		end
    		local returned = err
    		env = env
    		_G["progutils"] = env
    		
    		omi.main()
    	else
    		omi.crash("HTTP needs to be enabled.")
    	end
    end)
    if not ok then
    	if nTries == 3 then
    		print("The program has tried 3 times to get the API, We shall run shell instead.")
        	sleep(5)
        	shell.run("clear")
        	shell.run("shell")
        else
        	nTries = nTries + 1
            print("Program, failed to download API, Re-trying...")
            sleep(1)
            omi.init()
        end
    end
end

function omi.crash(errcode)
	col.screen("white")
	draw.box(1, 51, 1, 1, " ", "grey", "grey")
	draw.textc(" Omicron has crashed", 1, false, "cyan", "grey")
	local sText = "An error has caused Omicron to crash, the error is below, if this happens again, please report the problem and error code to DannySMc (dannysmc95). Error code is stated below in red."
	for k,v in ipairs(data.wordwrap(sText, 51)) do
		draw.texta(v, 1, k+2, false, "lightGrey", "white")
	end

	for k, v in ipairs(data.wordwrap(errcode, 45)) do
		draw.texta(v, 4, k+8, false, "red", "white")
	end

	draw.texta("-< Press enter to restart or 's' for shell >-", 4, 19, false, "grey", "white")


	while true do
		local args = { os.pullEvent() }
		if args[1] == "key" then
			if args[2] == keys.enter then
				omicron.main()
			elseif args[2] == keys.s then
				shell.run("clear")
				shell.run("shell")
			end
		end
	end
end

function omi.draw.loadanimation(intx, inty, txtcol, bkgcol)
	if nLoadAnim == 0 then
		draw.texta("|", intx, inty, false, txtcol, bkgcol)
		nLoadAnim = 1
	elseif nLoadAnim == 1 then
		draw.texta("/", intx, inty, false, txtcol, bkgcol)
		nLoadAnim = 2
	elseif nLoadAnim == 2 then
		draw.texta("-", intx, inty, false, txtcol, bkgcol)
		nLoadAnim = 3
	elseif nLoadAnim == 3 then
		draw.texta("\\", intx, inty, false, txtcol, bkgcol)
		nLoadAnim = 0
	end
end

function omi.draw.bar(screenname)
	draw.box(1, 51, 1, 2, " ", "grey", "grey")
	draw.texta("Omicron:", 1, 1, false, "cyan", "grey")
end

function omi.main()
	if term.isColour() then
		local ok, err = pcall(function()
			--omi.startup()
		end)
		if not ok then
			omi.crash(err)
		end
	else
		omi.crash("Please use an advanced (gold) computer.")
	end
end

function omi.startup()
	col.screen("black")
	sleep(0.15)
	col.screen("grey")
	sleep(0.15)
	col.screen("lightGrey")
	sleep(0.30)
	draw.textc(" Omicron Operating System", 4, false, "white", "lightGrey")
	sleep(0.30)
	omi.draw.loadanimation(26, 12, "white", "lightGrey")
	os.startTimer(0.15)

	while true do
		local args = { os.pullEvent() }
		if args[1] == "timer" then
			omi.draw.loadanimation(26, 12, "white", "lightGrey")
			os.startTimer(0.15)
		end
	end
end

function omi.test()
	for i=1, 10 do
		textutils.slowWrite(i, 0.5)
	end
end

thread:create("test", omi.test)

local th = thread:list()
print(th[1][1])
print(th[1][2])
print(th[1][3])

while true do
	for k,v in ipairs(thread:list()) do
		print(v[k][v])
		sleep(0)
	end
end

This was my test and it didn't work, could someone give me examples, as I have had many posts where people explain but I can't get it, so an example would be perfect, I know it's a lot to ask but I would be forever grateful!

Cheers anyone that reads and wants to help :)/>/>/>

I am on irc:
Link: http://webchat.esper.net
Channel: #shinexusuk

so you are welcome to help,

Once again, thanks to anyone that wants to help, I am very keen to learn just it has been months and I still can't figure it out.
Edited on 24 June 2015 - 09:42 AM
Bomb Bloke #2
Posted 24 June 2015 - 12:36 PM
  • Did you mean for your "thread" functions to refer to self.tRunning and self.sCurrent?
  • When searching for a certain process by means of ipairs, remember to break out of the loop once you find it - there's not point in looking further.
  • There's a couple of loops there where you compare "name" to "k", when you want "v".
  • Consider switching tRunning to a {["name"] = {window, coroutine, filter}} format, it'll make lookups much faster.
In regards to your coroutine-handling, I suspect you're still not understanding the event filtering system Dan's setup within ComputerCraft; I say this because you haven't defined a tRunning[4] to track the event each coroutine asks for when resumed. Read right through the script I composed here, then run it - it'll hopefully get the point across. If you don't get it, say so. I recommend experimenting with different "myFunc"s - try loadstring'ing a script or something in its place, and see what happens. Then try it again without my print statements. Experiment.

In regards to your app management, it's simply a case of having your menu bar function set a mouse_click filter whenever it yields - by eg having it call os.pullEvent("mouse_click"). Your coroutine manager (the one you're going to write) will then resume it whenever a mouse click occurs. The menu function itself will decide whether that click was on the bar, and if it wasn't, it'll simply call os.pullEvent("mouse_click") again; otherwise it can get busy creating a new "thread" or whatever with a thread:create() call.

Bear in mind that if your menu bar is up the top of your screen, then you'll be running your apps in a window located at least one row down from there - meaning you'll want to subtract one from the y-co-ord of every mouse_click event you resume those apps with.
Creator #3
Posted 24 June 2015 - 01:10 PM
You are making it too tableish. I believe this does not hinder actual code, but makes it hard to understand. Also, intead of switching while a routine is working, you can queue a special switch event, and handle it afterwards. Here is a working example of multitasking, you might wan't to take a look at it (thanks Bomb Bloke, you helped me a lot there). You might not want to include the functions directly into each thread, but rather, let them be objects that are manipulated by other function.
This:

resume(thread)
instead of this:

thread.resume()

Hope I helped you!
DannySMc #4
Posted 24 June 2015 - 01:23 PM
You are making it too tableish. I believe this does not hinder actual code, but makes it hard to understand. Also, intead of switching while a routine is working, you can queue a special switch event, and handle it afterwards. Here is a working example of multitasking, you might wan't to take a look at it (thanks Bomb Bloke, you helped me a lot there). You might not want to include the functions directly into each thread, but rather, let them be objects that are manipulated by other function.
This:

resume(thread)
instead of this:

thread.resume()

Hope I helped you!

Does this have an example in the code? Or is just like an API? If an API could you just show me like a simple os.pullEvent() example, the thing is when I am using os.pullEvent() as bomb bloke said above, It takes more than just "one" event, some of my programs, wait for up to 6 events, so I need to be able to just get the currently running process and hand all the events and arguments to that function…?

If that made any sense, I only really ask for examples is because I understand "how it works" I do not know how to implement it :(/>/> So an example allows me to see it actually working how I need it to then be able to go and see how it works, for yours creator, you said about thread.resume() I would rather that because I can name them and use them in the process switcher, I do not wish to make it wait for only one event, it needs to get all events, while it is the running process, as I will be having some coroutines that run with no GUI in the background, so what I want is complicated its just you guys are my only hope xD

Edit: Doesn't work, missing file…

Thanks again!
Edited on 24 June 2015 - 11:24 AM
Creator #5
Posted 24 June 2015 - 01:38 PM
Did youy loom at the link I provided you with? There it is all implemented an working. For example, to queue an event, you call, Internal.queueEvent(bla,ble).
Then it first goes through the events, processes them, and then handles the routines. Also, you can implement daemons with a parrallel system.
DannySMc #6
Posted 24 June 2015 - 01:39 PM
Did youy loom at the link I provided you with? There it is all implemented an working. For example, to queue an event, you call, Internal.queueEvent(bla,ble).
Then it first goes through the events, processes them, and then handles the routines. Also, you can implement daemons with a parrallel system.

Yeah I pasted it in a file and it errored with missing file…
Creator #7
Posted 24 June 2015 - 01:50 PM
Did youy loom at the link I provided you with? There it is all implemented an working. For example, to queue an event, you call, Internal.queueEvent(bla,ble).
Then it first goes through the events, processes them, and then handles the routines. Also, you can implement daemons with a parrallel system.

Yeah I pasted it in a file and it errored with missing file…

That is because it is part of OmniOS. :P/>
Forgot to mention that. If you want to see it in action, run the code in my signature.
DannySMc #8
Posted 24 June 2015 - 02:00 PM
I want these functions:

Creates a coroutine and a window and then sticks it into the running list
thread:create(name)

Resume a function by setting it as the main coroutine and then passing all arguments to that coroutine so this just literally changes the running process in the variable and redraws the window.
thread:resume(name)

List all running processes by name so I can use them to see what is running
thread:list()

Run all coroutines like a huge os.pullEvent() then when something happens it gets passed to the running function…? I think that is possible… So I will have a function that will os.pullEvent() then this returns to the main loop and then as it is set as the current process it will then thread:resume(name, args…) then that will take the args and give them back to the process that requested it?
thread:run()

I need an example like that, please? As that is what I see and then if someone makes them functions, I shall read through it, as I see it as if you have a new process it will add it to the the thread:run() so it yields all of them etc, so arguments know where to go, then when I want to change I resume another coroutine (this will be when an os.pullEvent() is called so I can use touch events to change threads, then it will redraw the window and go to the process and it should be in an os.pullEvent() so when it is resumed it just changes the name of the currently running process and then I can click on that screen? Something like that?



Did youy loom at the link I provided you with? There it is all implemented an working. For example, to queue an event, you call, Internal.queueEvent(bla,ble).
Then it first goes through the events, processes them, and then handles the routines. Also, you can implement daemons with a parrallel system.

Yeah I pasted it in a file and it errored with missing file…

That is because it is part of OmniOS. :P/>/>/>/>
Forgot to mention that. If you want to see it in action, run the code in my signature.

That's great but I need the basics and examples, thanks but it isn't what I need, I need examples, so i can test it myself and not in an os…? If that's okay… I shall post what I need below if you can do that and help me that would be awesome if not do not worry.

Edit the code is above at top of this comment of what I need.

Like I know you guys understand it but I need it to be like the format above so I can teach myself from the working example of what I need otherwise I won't learn as I can't do it and no one made an example, if possible the example I have said above wil be awesome even if it is a changed version of my code above? Please

Cheers,
Thanks sorry, I am struggling which is why I asked for examples, bomb bloke I get your code but I need a function to run all processes so I can switch between them quickly…
Edited on 24 June 2015 - 11:57 AM
DannySMc #9
Posted 24 June 2015 - 02:06 PM
Like to be able to create one and it go to it, but then being able to switch to it after..
DannySMc #10
Posted 24 June 2015 - 02:53 PM
Anyone?
Lupus590 #11
Posted 24 June 2015 - 04:17 PM
multi tasking is something that is underused in the community, help is likely limited
out side of using the parallel API and a few scraps of coroutines knowledge, i can't help either

also, your code is object orientated, another underused feature in the community
Edited on 24 June 2015 - 02:18 PM
DannySMc #12
Posted 24 June 2015 - 05:30 PM
Well thanks anyway :)/>
Creator #13
Posted 24 June 2015 - 08:38 PM
In my opinion, you shoild store all routines in a table and then call:


coroutine.resume(process[whatever].routine,unpack(event))
DannySMc #14
Posted 24 June 2015 - 08:39 PM
In my opinion, you shoild store all routines in a table and then call:


coroutine.resume(process[whatever].routine,unpack(event))

Yeah I still need a full example :(/> That's why I can't really get the hang of it as no one is willing to do that, it's cool I don't expect it just I have messed around with it so much but idk, it is so much easier to multitask in C++ xDDDDD
Edited on 24 June 2015 - 06:39 PM
MKlegoman357 #15
Posted 24 June 2015 - 10:53 PM
OK, so it looks like all that help a few months ago didn't help too much, did it? Maybe you needed another example of a coroutine manager, because that's the way you learn (everyone has their own way of learning). So here's a very simple coroutine manager for CC written by me just for you: pastebin.com/SN3aNziK. The actual manager is the function 'run'. Play around with it, make some functions, try to load some files.. like Bomb Bloke said:

Experiment.
DannySMc #16
Posted 25 June 2015 - 10:53 AM
OK, so it looks like all that help a few months ago didn't help too much, did it? Maybe you needed another example of a coroutine manager, because that's the way you learn (everyone has their own way of learning). So here's a very simple coroutine manager for CC written by me just for you: pastebin.com/SN3aNziK. The actual manager is the function 'run'. Play around with it, make some functions, try to load some files.. like Bomb Bloke said:

Experiment.

Well thank you, you are amazing! But I have two questions…

1. How do you add in a window / buffer? So when I resume a co routine it will redraw the window?

2. How do I switch between tasks,

So on every screen (each screen being a function) I have a menu bar that says what I am running,
How do I click on that to make it then go to another coroutine, so say I click home while I'm on my settings menu, how would I make it just flick to that one and keep the other one suspended?

Thanks for this I learnt quite a bit!:D/> I'm feeling happy now xD
Creator #17
Posted 25 June 2015 - 11:17 AM
Switching between task is no more than setting which process has a visible window and which events only get redireceted to the prcess ("mouse_click",…).

As for windows:

term.redirect(mythread.window)
--run
term.redirect(term.native())
DannySMc #18
Posted 25 June 2015 - 12:46 PM
Switching between task is no more than setting which process has a visible window and which events only get redireceted to the prcess ("mouse_click",…).

As for windows:

term.redirect(mythread.window)
--run
term.redirect(term.native())

That did not help at all… I need to know… how examples :(/>
Creator #19
Posted 25 June 2015 - 12:54 PM
Switching between task is no more than setting which process has a visible window and which events only get redireceted to the prcess ("mouse_click",…).

As for windows:

term.redirect(mythread.window)
--run
term.redirect(term.native())

That did not help at all… I need to know… how examples :(/>

First, you redirect to the window the process should draw to:

term.redirect(target)
Then you run the routine:

coroutine.resume(routine,unpack(event))
Then you have to redirect to the standart monitor again:

term.redirect(term.native())

You have to include that code in the run function.
Edited on 25 June 2015 - 10:55 AM
DannySMc #20
Posted 25 June 2015 - 01:34 PM
Switching between task is no more than setting which process has a visible window and which events only get redireceted to the prcess ("mouse_click",…).

As for windows:

term.redirect(mythread.window)
--run
term.redirect(term.native())

That did not help at all… I need to know… how examples :(/>/>

First, you redirect to the window the process should draw to:

term.redirect(target)
Then you run the routine:

coroutine.resume(routine,unpack(event))
Then you have to redirect to the standart monitor again:

term.redirect(term.native())

You have to include that code in the run function.

I know that its getting it all together? and being able to switch when a coroutine is running…
Bomb Bloke #21
Posted 25 June 2015 - 04:25 PM
The problem with showing you examples is that you just ask for another example. Take multishell, for instance - it does what you're asking, and very nearly nothing else. The file from OmniOS that Creator linked you to is a near perfect match to your request. I haven't read MKlegoman357's link, but I'll bet that covers everything you're asking as well. But you'll notice they aren't short or simple examples, because you're asking how to do many things at once.

So how about you just try writing the code. You'll want to do it in stages: Stage one being to write a function which runs some other function (just one!) via the coroutine API. Forget about everything else you want to do, at least for now - just get that done, post a link to the paste, then we'll talk about stage two. Heck, feel free to copy'n'paste from the code example I linked you to above, that contains +90% of what you need for this first bit.
DannySMc #22
Posted 25 June 2015 - 06:30 PM
The problem with showing you examples is that you just ask for another example. Take multishell, for instance - it does what you're asking, and very nearly nothing else. The file from OmniOS that Creator linked you to is a near perfect match to your request. I haven't read MKlegoman357's link, but I'll bet that covers everything you're asking as well. But you'll notice they aren't short or simple examples, because you're asking how to do many things at once.

So how about you just try writing the code. You'll want to do it in stages: Stage one being to write a function which runs some other function (just one!) via the coroutine API. Forget about everything else you want to do, at least for now - just get that done, post a link to the paste, then we'll talk about stage two. Heck, feel free to copy'n'paste from the code example I linked you to above, that contains +90% of what you need for this first bit.

Not so sound rude, I am only asking for examples, is because I don't want to edit it, i need it to work as I then try and mess around with it to see it in action… Trust me I liked multishell, but I don't know how to edit it to work for me, I am no good with all of these coroutines and buffers, I just want an all in one so I can just pull it apart and see how it works myself, seeing other code in someone else's program doesn't help, with Creator, it didn't work it just errored…

I get I am a pain and that is why I asked for an example of a buffer/coroutine system that will allow me to switch between two screens so for example make a screen and then test it etc etc, I am playing around with the one MKLegoman gave me and it's amazing but I don't know how to rig in the window api etc, and I don't know how I am able to switch a task and add names for them so I know what they are like the one I posted above, tis why I asked for the example.

I have the code, what MKLegoman gave me works and does that, it grabs the correct events, I asked on a quote of his post how to add a window buffer in there and a way of naming them, and then when I say make a touch interface to be able to click a task (like a task manager) and then go to that function… idk it is confusing as hell, I can do all the http stuff and vectors but this is just confusing… :(/> Thanks for your help:

here is the code MKLegoman made for me:
http://pastebin.com/SN3aNziK

Cheers
MKlegoman357 #23
Posted 25 June 2015 - 10:50 PM
So I'll try to help you write the code simply by asking you questions. You'll have to try to answer them and write some code (you may append the code to my given coroutine manager, just post the code here after you edit it)

We'll start by adding a buffer to the threads (that's how I'll call the objects that will have the coroutine, a name, a buffer, etc..). We'll need to show only the buffer of our currently running coroutine. But we don't have a system currently setup to indicate which coroutine is currently running, do we? So my first question would be: what defines a currently running coroutine and how's it different from not currently running coroutines? (think about certain events, the buffer, list all the differences)
Edited on 25 June 2015 - 08:51 PM
DannySMc #24
Posted 25 June 2015 - 11:43 PM
So I'll try to help you write the code simply by asking you questions. You'll have to try to answer them and write some code (you may append the code to my given coroutine manager, just post the code here after you edit it)

We'll start by adding a buffer to the threads (that's how I'll call the objects that will have the coroutine, a name, a buffer, etc..). We'll need to show only the buffer of our currently running coroutine. But we don't have a system currently setup to indicate which coroutine is currently running, do we? So my first question would be: what defines a currently running coroutine and how's it different from not currently running coroutines? (think about certain events, the buffer, list all the differences)

From what I know there is no difference other than a variable saying what coroutine is running… Therefore any events you get need to go to the running coroutine and none of the others…?

I would say that as they are all running at once the buffer is what will redraw everytime you change coroutine, for each individual screen…?
Bomb Bloke #25
Posted 26 June 2015 - 12:00 AM
Therefore any events you get need to go to the running coroutine and none of the others…?

Not quite right; if you don't resume a coroutine, then it sits there doing nothing. If you want to run all your coroutines at once, that means that with every event your main script pulls, you'll be wanting to resume all your coroutines.

There are exceptions to this - you'll probably only want to pass "user input" events, such as key, key_up, mouse_click, etc, to the "current" coroutine. But for everything else - redstone state changes, rednet messages, etc, it's essential that they be passed on all the time. Have a think about which event types would be for the "current coroutine" only, and which would be for all.
DannySMc #26
Posted 26 June 2015 - 12:13 AM
Therefore any events you get need to go to the running coroutine and none of the others…?

Not quite right; if you don't resume a coroutine, then it sits there doing nothing. If you want to run all your coroutines at once, that means that with every event your main script pulls, you'll be wanting to resume all your coroutines.

There are exceptions to this - you'll probably only want to pass "user input" events, such as key, key_up, mouse_click, etc, to the "current" coroutine. But for everything else - redstone state changes, rednet messages, etc, it's essential that they be passed on all the time. Have a think about which event types would be for the "current coroutine" only, and which would be for all.

Ahh yes, didn't even think about that I guess, I would like one coroutine to deal with redstone, rednet etc, and then user interaction should be sent to the running process, although how do you make it so I can have redstone as well?

To be honest I haven't even got the code I a just wondering as I have no idea how to do this :P/>
DannySMc #27
Posted 26 June 2015 - 09:35 AM
Hey guys I added this to add the buffer in, and I attempted at making a Resume Function but it didn't work, the code posted below is what I am using, if possible could someone fix it so I can like switch threads? Because currently, they all run and I want to be able to run one, and then be able to spawn running threads but only want stuff to go to that running thread…? and then I wish to be able to resume a coroutine for my actual task manager?

Spoiler
--// The coroutine manager | a sample code for DannySMc | by MKlegoman357 \\--

-- this table holds all the coroutines in the format:
-- routines = {
--   {
--     name = name of the coroutine;
--     func = the function of the coroutine;
--     co = the coroutine;
--     filter = the event filter for the coroutine;
--   },
--   ...
-- }
aa = aa or {}
local a = http.get("https://vault.dannysmc.com/lua/api/dannysmcapi.lua")
a = a.readAll()
local env = {}
a = loadstring(a)
local env = getfenv()
setfenv(a,env)
local status, err = pcall(a, unpack(aa))
if (not status) and err then
    printError("Error loading api")
    return false
end
local returned = err
env = env
_G["snet"] = env

local routines = {}

-- generic function to add a thread, it's not really needed, just here as a 'helper' function
local function addThread (name, func)
  routines[#routines + 1] = {
    name = name;
    func = func;
    co = coroutine.create(func);
    filter = nil;
    buf = window.create(term.native(), 1, 1, 51, 19, false)
  }
end

-- this functions simply removes the coroutines by their name
local function removeThread (name)
  local toRemove = {}

  for i, thread in ipairs(routines) do
    if thread.name == name then
      toRemove[#toRemove + 1] = i
    end
  end

  for i = #toRemove, 1, -1 do
    table.remove(routines, toRemove[i])
  end
end

local function resumeThread(name)
  for i, thread in ipairs(routines) do
    if thread.name == name then
      thread["buf"].setVisible(true)
      thread["buf"].redraw()
      term.redirect(thread.buf)
      local ok, filter = coroutine.resume(thread.co, unpack(e)) -- resume the coroutine with the event data

      if not ok then -- if the coroutine errored, print the error
        error(filter, 2)
      end
    end
  end
end

local function run ()
  -- e is a table holding all the event data; event is simply the event string (mouse_down, key, terminate, ...)
  local e, event = {}, nil -- we will have to run the coroutines first, so we'll set the event to be empty

  while #routines > 0 do -- while there are coroutines to run
    -- this table will hold the index of the coroutines which are dead, so we'll be able to remove them later
    local toRemove = {}

    for i, thread in ipairs(routines) do -- for every thread (coroutine, routine - call it whatever)
      -- there are ALWAYS 3 conditions on when to resume a coroutine in ComputerCraft (only one of them has to be true):
      -- 1. the event is 'terminate'
      -- 2. the event filter of the coroutine is nil
      -- 3. the event filter is equal to the coroutine's event filter
      if event == "terminate" or thread.filter == nil or thread.filter == event then
        thread["buf"].setVisible(true)
        thread["buf"].redraw()
        term.redirect(thread.buf)
        local ok, filter = coroutine.resume(thread.co, unpack(e)) -- resume the coroutine with the event data

        if not ok then -- if the coroutine errored, print the error
          error(filter, 2)
        end

        -- if the coroutine ended, add it to remove list
        if coroutine.status(thread.co) == "dead" then
          toRemove[#toRemove + 1] = i
        end

        -- don't forget to set the coroutine's filter! It's the same thing that you pass to os.pullEvent( filter )
        thread.filter = filter
      end
    end

    for i = #toRemove, 1, -1 do -- remove all the dead coroutines
      table.remove(routines, toRemove[i])
    end

    -- if there are no more coroutines then break out of the loop. This if is necessary because otherwise the os.pullEventRaw line would be called
    if #routines == 0 then
      break
    end

    -- catch any events and restart the loop to resume the coroutines
    e = {os.pullEventRaw()}
    event = e[1]
  end
end

--[[ Test code ]]--

local function main ()
  print("main")
  sleep(1)
  print("main done")
end

local function bar ()
  col.screen("white")
  draw.box(1, 51, 1, 2, " ", "grey", "grey")
  for i=1, 10 do
    draw.texta(i, 1, i+3, false, "cyan", "white")
  end

  draw.texta(misc.time(), 47, 1, false, "lime", "grey")
  print("Press '1' to exit")
  print("Press '2' to go to eh")

  while true do
    local args = { os.pullEvent() }
    if args[1] == "timer" then
      draw.texta(misc.time(), 47, 1, false, "lime", "grey")
    elseif args[1] == "char" then
      if args[2] == "1" then
        break
      elseif args[2] == "2" then
        resumeThread("foo")
      end
    end
  end
end

local function eh()
  print("running: 'eh'")
  for i=1, 10 do
    print(i)
  end
end

local function foo ()
  sleep(1)
  print("foo")
  sleep(1)
  addThread("bar", bar)
end

addThread("main", main)
addThread("foo", foo)
addThread("eh", eh)

print("START")

run()

print("END")
DannySMc #28
Posted 26 June 2015 - 09:56 AM
The post above has what I have done, the buf works kinda, the resume doesn't neither does the other things I tried xD
Bomb Bloke #29
Posted 26 June 2015 - 10:04 AM
Bear in mind that the "run" function does not return (therefore allowing the script to continue to the final print("END") line) until all the added threads have executed to completion. Given that run() may automatically resume foo() whenever bar() yields (which bar() does every time it needs to pull an event), is there really any need for bar() to attempt to resume foo() manually?

You've more or less got the idea with the buffers, but it's worth noting that you don't need to make a window visible unless you want its contents to actually become visible to the user (likewise with redrawing). Odds are you're going to want only one buffer visible at once, no matter what the other coroutines are doing. Forgetting, for the minute, how you might "change" which your active coroutine is, try rigging things up so that you've got a variable which at least defines which it is, and makes it so that only that window is visible to the user.
Edited on 26 June 2015 - 08:05 AM
DannySMc #30
Posted 26 June 2015 - 10:17 AM
Bear in mind that the "run" function does not return (therefore allowing the script to continue to the final print("END") line) until all the added threads have executed to completion. Given that run() may automatically resume foo() whenever bar() yields (which bar() does every time it needs to pull an event), is there really any need for bar() to attempt to resume foo() manually?

You've more or less got the idea with the buffers, but it's worth noting that you don't need to make a window visible unless you want its contents to actually become visible to the user (likewise with redrawing). Odds are you're going to want only one buffer visible at once, no matter what the other coroutines are doing. Forgetting, for the minute, how you might "change" which your active coroutine is, try rigging things up so that you've got a variable which at least defines which it is, and makes it so that only that window is visible to the user.

Yeah I shall do, I am gonna post the code here:
https://code.stypi.com/dannysmc95/Multitasking/multitasking.lua

This allows you to code with me at the same time, if you have time to help that would be great, I am currently at work but I can still come on and off it to answer questions and do what I think is right :P/>

Of course like I said any help is greatly appreciated!
DannySMc #31
Posted 26 June 2015 - 10:25 AM
MKLegoman, if you can come on stypi (link above) and you could help as creator doesn't get it.. so yeah :/
Creator #32
Posted 26 June 2015 - 11:22 AM
MKLegoman, if you can come on stypi (link above) and you could help as creator doesn't get it.. so yeah :/

I do get the whole coroutine and multitasking stuff. It is just that your code is kinda messy. Don't be mad, you are here to learn. ;)/>
DannySMc #33
Posted 26 June 2015 - 11:54 AM
MKLegoman, if you can come on stypi (link above) and you could help as creator doesn't get it.. so yeah :/

I do get the whole coroutine and multitasking stuff. It is just that your code is kinda messy. Don't be mad, you are here to learn. ;)/>/>

You may want to tell MKLegoman that his code is messy… It is not my code it is his… So you are welcome to tell him about that..
Creator #34
Posted 26 June 2015 - 07:21 PM
MKLegoman, if you can come on stypi (link above) and you could help as creator doesn't get it.. so yeah :/

I do get the whole coroutine and multitasking stuff. It is just that your code is kinda messy. Don't be mad, you are here to learn. ;)/>/>

You may want to tell MKLegoman that his code is messy… It is not my code it is his… So you are welcome to tell him about that..

Well, it is! Or I don't get it. But in my opinion it is not the right approach. It is you coroutine manager after all.
MKlegoman357 #35
Posted 26 June 2015 - 08:04 PM
Well, it is! Or I don't get it. But in my opinion it is not the right approach. It is you coroutine manager after all.

Well, my code is the one I posted a pastebin link to, his code, I suppose is the one you're referring to, is in the first post.
Creator #36
Posted 26 June 2015 - 09:50 PM
Well, it is! Or I don't get it. But in my opinion it is not the right approach. It is you coroutine manager after all.

Well, my code is the one I posted a pastebin link to, his code, I suppose is the one you're referring to, is in the first post.

Yeah, sorry. I meant yours and then Danny is learning from it.
DannySMc #37
Posted 27 June 2015 - 12:13 AM
Anyway I have learnt thanks to a nice 2 hour long session with MKLegoman :P/>