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

Need help with parallel to pause a function/coroutine while a variable is set

Started by cyanisaac, 19 July 2015 - 07:58 PM
cyanisaac #1
Posted 19 July 2015 - 09:58 PM
Hello! My OS uses the parallel API to run multiple things at once (for instance, a home button bar and a shell area). I am looking for a way to be able to completely pause the execution of the shell area while my home button row does a popup application. I would like to do this in a fashion similar to the parallel API and I would like to be able to pause this thread based on a variable.

In case that sounds confusing, I'll make it less confusing: I need to pause specific threads if a variable is set true and resume them once said variable is false.


Also I have no idea how the coroutine API works that's why im using parallel pls don't kill me.

This is the code in my OS:

function backgroundProcesses()
		function flightBar()
				shell.run("/OTOS/flightBar")
		end

		function otherBackgroundProcesses()
				shell.run("/OTOS/background")
		end

		term.redirect(shellArea)
		parallel.waitForAny(flightBar, otherBackgroundProcesses)
end
This runs the background processes including my "flight bar" which is my home button row.

This is my other bit of code:

function runOTOScore()
		print("Running preliminary processes...")
		shell.run("OTOS/._preliminary")
		print("Grabbing version files...")
		fs.delete("/version")
		shell.run("pastebin get ZxBUvTSG version")
		print(shell.run("/OTOS/core"))
		os.unloadAPI("OTOS")
end
This is the thing I need to be able to pause, and have it not resume until a variable is triggered, since it contains the shell area.

If anyone can help me this would be greatly appreciated.
H4X0RZ #2
Posted 19 July 2015 - 10:43 PM
Were exactly do you run the shell?
Edited on 19 July 2015 - 09:27 PM
cyanisaac #3
Posted 19 July 2015 - 11:04 PM
Were exactly to you run the shell?

See file here: http://pastebin.com/E6vvsijn
At lines 86 and 101:

if OTOS.isLoaded then
			    shellArea = window.create(term.current(), 1, 1, 51, 18)
			    infoBar = window.create(term.current(), 1, 19, 51, 1)
			    parallel.waitForAny(runOTOScore, backgroundProcesses, fixShellArea)
else
  --Whatever not important
Bomb Bloke #4
Posted 20 July 2015 - 02:37 AM
Long story short, the vanilla parallel API can't do this.

It can certainly be done, but yes, an understanding of how to manage coroutines is required. Then there's the whole matter of what to do with all the events that you aren't using to resume the "paused" coroutines (timer events in particular should ideally remain in-sync) - Dan probably implemented the best solution within multishell, which was to never pause them at all, but instead to merely deny them access to user input events when they aren't in focus. It'd be relatively easy to build a custom version of the parallel API that does that.
cyanisaac #5
Posted 20 July 2015 - 03:56 AM
Could you build one that does that? I am very unfamiliar with how coroutines work and I don't know how to disable user input to a specific parallel running process.
KingofGamesYami #6
Posted 20 July 2015 - 05:28 AM
I'm probably going to screw this up somewhere, but here goes:

Spoiler

local tUserInput = {
  ["key"] = true,
  --#add stuff like mouse_click, char, monitor_touch, etc. here
}

local tRunning = {}

local maxID = 0

local function addToRunning( func ) --#simple.  Just pass this a function, it adds it to the table.
  local co = coroutine.create( func )
  tRunning[ #tRunning + 1 ] = { co = co, id = maxID + 1, bUserInput = false }
  maxID = maxID + 1
  return maxID --#return something to identify
end

local function setUserInput( id, b )
  for k, v in pairs( tRunning ) do --#find a thread that matches the id
    if v.id == id then
      v.bUserInput = b --#set the user input (true for "I want this to receive all events", false for "filter out user input")
      return true
    end
  end
  return false, "Not a valid coroutine ID" --#if it's not found, report the failure.  I didn't want to error, because that causes problems.
end

local function runAll() --#this works like parallel.waitForAll, it'll run until it has no more coroutines
  local event = {}
  while ( #tRunning > 0 ) do --#tweak this limit to change the behavior to match parallel.waitForAny
     for k, v in pairs( tRunning ) do
        if (event[ 1 ] == v.filter or not v.filter) and (v.bUserInput or not tUserInput[ event[ 1 ] ]) then --#if the coroutine doesn't have a filter or the event in queue matches the filter AND the program accepts user input or it wasn't a user input event,
          local ok, v.filter = coroutine.resume( v.co, unpack( event ) ) --#resume the coroutine, set the filter to what's returned by coroutine.yield (this is how the event system works)
          if coroutine.status( v.co ) == "dead" then --#if the coroutine has finished
            table.remove( tRunning, k ) --#remove it
          end
        end
     end
     event = { os.pullEvent() }
  end
end

I'd not bet on this working the first time, but I'm sure you can see where I'm going with it. Coroutines aren't really very difficult to work with.

PS: You could get rid of the locals and load the above as an API