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

Coroutine management?

Started by Creator, 04 March 2015 - 05:50 PM
Creator #1
Posted 04 March 2015 - 06:50 PM
About coroutines, I have written this code:
Spoiler

--Kernel--
--Variables--
local routines = {}
local arguments = {}
local args = {...}
local _funcPath = args[1]
local _title = args[2]
table.remove(args,2)
table.remove(args,1)
--Functions--
function getPermission(base,program)
local f = fs.open(base.."/"..program..".permission")
local m = f.readAll()
f.close()
return m
end
function newProcess(funcPath,title,_arguments)
local routineIsPresent = false
local func
local buffer = {}
local permission = nil
local base = ""
local program = nil
local environement = {}
routines[title] = ""
for i,v in pairs(routines) do
  if i == title then
   routineIsPresent = true
  end
end
if routineIsPresent then return false end
if type(funcPath) == "string" then
  func = loadfile(funcPath)
else return false end

for token in string.gmatch(funcPath,"(/[^/]+)") do
  buffer[#buffer+1] =  token
end
for i = 1, #buffer-1 do
  base = base..buffer[i]
end
program = buffer[#buffer]
if buffer[#buffer-2] == "Programs" then
  permission = getPermission(base,program)
  environement = Sandbox.newEnvironement(funcPath,permission)
else
  setmetatable(environement,{_index = _G})
end
routines[title] = coroutine.create(func)
arguments[title] = unpack(_arguments)
end
function coroutineManager()
while true do
  for i,v in pairs(routines) do
   print(v)
   os.pullEvent()
   coroutine.resume(v,arguments[i])
  end
end
end
newProcess(_funcPath,_title,_arguments)
for i,v in pairs(routines) do
print(i)
end
os.pullEvent()
coroutineManager()



And it throws an error at line 60


coroutine.resume(v,arguments[i])

So I don't get what is wrong and I am sure that if we try to fix this here together, DannySMc could benefit from this too.

PS: I did not want to start another topic that discusses the exaclty same thing and I read through the tutorial and many other tutorials and inclusively looked at multishell. If you want to look at the code here it is: TheOS.

Thanks in advance and I hope we can help the comunity!

~Creator
Lyqyd #2
Posted 04 March 2015 - 07:55 PM
Split into new topic.

Obviously, you can both read each other's topic as they progress, but it will be much easier for those helping to track what's going on with the two separate people having their own topic.
Creator #3
Posted 04 March 2015 - 07:59 PM
Split into new topic.

Obviously, you can both read each other's topic as they progress, but it will be much easier for those helping to track what's going on with the two separate people having their own topic.

Thanks, I guess you are right in some way. But that does still not answer my question. ;)/>
Bomb Bloke #4
Posted 04 March 2015 - 09:42 PM
You neglected to mention the content of your error. But at a glance:

arguments[title] = unpack(_arguments)

"_arguments" will always be nil, and even if it were a suitable table, this would do the same thing:

arguments[title] = _arguments[1]

Generally, one would want to resume coroutines with event data, however.
Creator #5
Posted 04 March 2015 - 10:00 PM
You neglected to mention the content of your error. But at a glance:

arguments[title] = unpack(_arguments)

"_arguments" will always be nil, and even if it were a suitable table, this would do the same thing:

arguments[title] = _arguments[1]

Generally, one would want to resume coroutines with event data, however.

Thanks, however I do not think that is the problem. The error it throws is "Thread expected, got string"
Bomb Bloke #6
Posted 04 March 2015 - 11:02 PM
routines[title] = ""
for i,v in pairs(routines) do
  if i == title then
   routineIsPresent = true
  end
end
if routineIsPresent then return false end

This adds an "title" key to your "routines" table, checks to see if it's already in there, then returns false when it inevitably finds that it is. Switch the order around a little.
Creator #7
Posted 05 March 2015 - 01:10 PM
I rewrote the whole kernel and this is the result


--Kernel--
--Variables--
local routines = {}
local currRoutine = nil
local args = {...}
local funcPath = args[1]
local title = args[2]
table.remove(args,2)
table.remove(args,1)
--Functions--
local function getPermission(base,program)
local f = fs.open(base.."/"..program..".permission")
local m = f.readAll()
f.close()
return m
end
local function parsePath(_funcPath)
local buffer = {}
for token in string.gmatch(_funcPath,"[^/]+") do
  buffer[#buffer + 1] = token
end
return buffer
end
local function getEnvironment(nTable,_path)
if nTable[#nTable-3] == "Programs" then
  local b = fs.open(_path..".permissions")
  local tEnv = b.readLine()
  b.close()
  if tEnv == "admin" then
   local mEnv = {}
   setmetatable(mEnv,{_index = _G})
   return mEnv
  else
   Sandbox.newEnvironement(_path, "semiadmin")
  end
else
  local mEnv = {}
  setmetatable(mEnv,{_index = _G})
  return mEnv
end
end
function newProcess(_funcPath,_title,_arguments)
local path = parsePath(_funcPath)
local env = getEnvironment(path,_funcPath)
for i,v in pairs(routines) do
  if v == title then return false end
end
routines[title] = ""
local function toRun()
  os.run(env,_funcPath,unpack(_arguments))
end
routines[title] = coroutine.create(toRun)
end
local function coroutineManager()
while true do
os.pullEvent()
  for i,v in pairs(routines) do
   term.clear()
   term.setCursorPos(1,1)
   print("Resuming coroutine...")
   print(i)
   print(v)
   os.pullEvent()
   coroutine.resume(v)
  end
end
end
newProcess(funcPath,title,arguments)
term.clear()
term.setCursorPos(1,1)
print("Booting into multitasking...")
os.pullEvent()
coroutineManager()

It's not working and I can't figure out why.

Any help is appreciated ;)/>

~Creator
KingofGamesYami #8
Posted 05 March 2015 - 02:09 PM

while true do
os.pullEvent() --#discard an event from the queue
  for i,v in pairs(routines) do --#loop through the coroutines
   term.clear()
   term.setCursorPos(1,1)
   print("Resuming coroutine...")
   print(i)
   print(v)
   os.pullEvent() --#discard another event
   coroutine.resume(v) --#resume the coroutine without passing any event information
  end
end

…begin to see the problem?
Creator #9
Posted 05 March 2015 - 02:56 PM
What changes should I apply to the code?
KingofGamesYami #10
Posted 05 March 2015 - 03:15 PM
1. Stop pulling events inside the for loop
2. Actually store the data from events outside the for loop, but inside the while loop
3. Keep track of the returns from coroutine.resume, so you know when to apply a filter to events.
4. Filter events using the data from #3, giving the appropriate events to the coroutine when it resumes.



This is just what I got on that particular part of your code
Creator #11
Posted 05 March 2015 - 03:59 PM
1. Stop pulling events inside the for loop
2. Actually store the data from events outside the for loop, but inside the while loop
3. Keep track of the returns from coroutine.resume, so you know when to apply a filter to events.
4. Filter events using the data from #3, giving the appropriate events to the coroutine when it resumes.



This is just what I got on that particular part of your code

How do I do the third step and it appears to never resume the coroutine because the desktop app is set to turn the screen purple, but it never happens.
KingofGamesYami #12
Posted 05 March 2015 - 04:08 PM

    local tFilters = {}
    local eventData = {}
    while true do
    	for n=1,count do
    		local r = _routines[n]
    		if r then
    			if tFilters[r] == nil or tFilters[r] == eventData[1] or eventData[1] == "terminate" then
	    			local ok, param = coroutine.resume( r, unpack(eventData) )
					if not ok then
						error( param, 0 )
					else
						tFilters[r] = param
					end
					if coroutine.status( r ) == "dead" then
						_routines[n] = nil
						living = living - 1
						if living <= _limit then
							return n
						end
					end
				end
    		end
    	end

^^ thats how the parallel api does it.
Bomb Bloke #13
Posted 05 March 2015 - 10:13 PM
Though there's a little more to it than that (eg - pulling the event data to pass to the coroutines!). Better to read the original source file and get the full picture.

How do I do the third step and it appears to never resume the coroutine because the desktop app is set to turn the screen purple, but it never happens.

When the desktop app yields, it may be calling coroutine.yield() with a parameter, which it probably intends to be an event filter. For example, it might use "char", "key", "timer", or any of the other event names. coroutine.resume() passes these parameters back to the parent process as return values.

You need to ensure that when your coroutine manager detects such an event, that it passes that event data back to the desktop app when it coroutine.resume()'s it. You also need to ensure that, if an event filter was specified, you don't resume the desktop app until the appropriate event appears in the queue.

If you simply resume the desktop app at any time, it'll likely break it. If you resume the desktop app without ever passing it any event data, it'll most certainly break it.
Edited on 05 March 2015 - 09:14 PM
Creator #14
Posted 05 March 2015 - 10:54 PM
Though there's a little more to it than that (eg - pulling the event data to pass to the coroutines!). Better to read the original source file and get the full picture.

How do I do the third step and it appears to never resume the coroutine because the desktop app is set to turn the screen purple, but it never happens.

When the desktop app yields, it may be calling coroutine.yield() with a parameter, which it probably intends to be an event filter. For example, it might use "char", "key", "timer", or any of the other event names. coroutine.resume() passes these parameters back to the parent process as return values.

You need to ensure that when your coroutine manager detects such an event, that it passes that event data back to the desktop app when it coroutine.resume()'s it. You also need to ensure that, if an event filter was specified, you don't resume the desktop app until the appropriate event appears in the queue.

If you simply resume the desktop app at any time, it'll likely break it. If you resume the desktop app without ever passing it any event data, it'll most certainly break it.

Thanks. It appears to be working now, with only one active process ;)/>