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

Terminating one function running in parallel

Started by Kizz, 11 July 2014 - 02:04 PM
Kizz #1
Posted 11 July 2014 - 04:04 PM
Hai again,

I hate to blow up the forums, but I have one more, probably simple, question.

Say I have two functions running in parallel:


parallel.waitForAny(scch, main)

Say main is running in the background, taking care of business, while SCCH is waiting for user input. One of the user-inputs is to shut the system down, but it warns first.

Is there a way to terminate the main function whilst the SCCH function continues with the shutdown process?
LBPHacker #2
Posted 11 July 2014 - 04:18 PM
main is running in the background, taking care of business, while SCCH is waiting for user input
It's important to know that when SCCH is waiting for user input, so is main. When there is user input, both main and SCCH are notified about it (and main might not care about it). The easiest way I can think of is emitting an event that tells main to stop:
-- in SCCH
os.queueEvent("stop_main")
os.pullEvent("stop_main") -- or coroutine.yield("stop_main") or whatever you prefer
-- in main
while true do
    local eventData = {os.pullEvent()} -- I just hope this is the method you use for pulling events; you showed us no code
    if eventData[1] == "stop_main" then
        return
    elseif ...

That way SCCH fires the stop_main event, pauses, main stops completely and SCCH gets resumed. This won't work though if you use parallel.waitForAny, because the moment main stopped, .waitForAny would happily kill SCCH as well. Use parallel.waitForAll.
Edited on 11 July 2014 - 02:34 PM
Kizz #3
Posted 11 July 2014 - 04:36 PM

--MAIN LOOP for REFRESH

function main()

while true do
--Screen choice chk
--Power system checks/screen
	  

--Server Checks/Screen
chksvr(1,1,2)
chksvr(3,2,2)
chksvr(5,3,2)
	
--loop refresh pause
end

end

function scch()

print'Welcome to the Screen Server'
print'Please select an option:'
print'1: Server Monitor'
print'2: Power Systems Monitor'
print'3: System Control Touch Monitor'
print'4: System Shutdown'

sel=io.read()
sel=sel+1-1

if sel==1 then
print'Currently active.'
end

if sel==2 then
print'Coming soon.'
end

if sel==3 then
print'Coming soon.'
end

if sel==4 then
print'Shutting down!'
--os.queueEvent("stop_main")
--os.pullEvent("stop_main")
screen.clear()
screen.setCursorPos(1,1)
ps("Warning! System Shutting Down!")
sleep(5)
screen.clear()
os.shutdown()
end
end

parallel.waitForAll(scch, main)
--end main

There is my code. I tried, and failed, to implement your solution. I see how it would work, but I'm doing something wrong.

I also tried using an if statement to listen for the public variable of sel==4. This worked, but the problem is that the function has to complete what it is doing before it looks for the selection. Meaning it will display server status, when I want it to stop completely. is there no way to kill that specific process/thread or whatever it would be called?
Edited on 11 July 2014 - 02:41 PM
LBPHacker #4
Posted 11 July 2014 - 04:50 PM
It just hangs and dies, right? (Too long without yielding). It's because main doesn't ever yield, and ComputerCraft doesn't like that. I see you misunderstand how coroutines work.

It's important to know that when SCCH is waiting for user input, so is main.
There's no way to actually run more than one functions parallelly in CC. You can manage coroutines (get event data, pass the data to the first coroutine, wait until it yields, pass the data to the second coroutine, etc.) but you can't make more than one run at a time. Here's how coroutines work.

That settled, you might now that main should look something like this:
function main()
    --[[ I wasn't sure about the code in this block
        --Screen choice chk
        --Power system checks/screen
        --Server Checks/Screen
        chksvr(1,1,2)
        chksvr(3,2,2)
        chksvr(5,3,2)
        --loop refresh pause
    --]]
    -- do initialization here
    while true do
        local eventData = {os.pullEvent()}
        if eventData[1] == "stop_main" then
            return
        -- elseif eventData[1] == "..." then
        end
        -- process eventData
        -- do things here you want to be done every time an event fires
    end
end

EDIT: *sigh* These "awesome" code blocks don't like comments. For some reason, they think that we're posting BASIC code snippets and everything that begins with an apostrophe is a comment.
Edited on 11 July 2014 - 02:52 PM
Kizz #5
Posted 11 July 2014 - 05:24 PM
Thanks a bunch buddy. Using some knowledge I learned earlier on, I was able to get it to work. Here is what I did:


function main()
    while true do
    timeout = os.startTimer(.5)
	    local eventData = {os.pullEvent()}
	    if eventData[1] == "stop_main" then
		    break
		    elseif eventData[1] == "timer" then
   --Server Checks/Screen
   chksvr(1,1,2)
   chksvr(3,2,2)
   chksvr(5,3,2)
   --Power system checks/screen
  
  
  
   --Screen choice chk
	    end	 
    end
end

I used break rather than return, as I want to totally leave the main function.

I also call the event earlier:


if sel==4 then
    print'Shutting down!'
    os.queueEvent("stop_main")
    os.pullEvent("stop_main")
    sleep(1)
    screen.clear()
    screen.setCursorPos(1,1)
    ps("Warning! System Shutting Down!")
    sleep(5)
    screen.clear()
    os.shutdown()
end

By doing this, it will kill the main function, then continue on with the shutdown warning and finally the shutdown.

Thanks a ton man! Learning every day.
LBPHacker #6
Posted 11 July 2014 - 05:30 PM
Yup, that's it. One thing though: using return is "more totally leaving the function" than using break is, as break only leaves the topmost looping structure (in this case, the while loop), while return actually leaves the function itself.
Kizz #7
Posted 11 July 2014 - 05:43 PM
Odd, for some reason it doesn't seem to work. It seems to run:


elseif eventData[1] == "timer" and eventData[2] == timeout2 then
   --Server Checks/Screen
   chksvr(1,1,2)
   chksvr(3,2,2)
   chksvr(5,3,2)
   --Power system checks/screen
  
  
  
   --Screen choice chk
		end	

no matter what I do…

It seems as though it returns, then re-runs the function. Like it never breaks away :(/>
Edited on 11 July 2014 - 03:44 PM
LBPHacker #8
Posted 11 July 2014 - 05:53 PM
Right. What's in then chksvr function?
Kizz #9
Posted 11 July 2014 - 06:02 PM

function chksvr(sendchan,svrnum,gettmout)
send(sendchan,2,"ping")
get(2,gettmout)
if success==1 then
screen.setCursorPos(1,svrnum+2)
w("Server "..svrnum..": Online")
end
if success==0 then
screen.setCursorPos(1,svrnum+2)
w("Server "..svrnum..": DOWN")
end
--sleep(5)
end

It basically sends a ping to a server, and listens real quick for a return ping.

I modified it so that the timer can only run if key is 1 and it still seems to freaking run chksvr…


function main()
key=1
    while true do
if key==1 then
timeout2 = os.startTimer(.5)
end
    eventData = {os.pullEvent()}
	    if eventData[1] == "stop_main" then
   key=0
		    return
		    elseif eventData[1] == "timer" and eventData[2] == timeout2 then
   --Server Checks/Screen
   chksvr(1,1,2)
   chksvr(3,2,2)
   chksvr(5,3,2)
   --Power system checks/screen
  
  
  
   --Screen choice chk
	    end	 
end
end

It's as if it never sees the stop_main event…

I've found the issue, but not the solution:

It seems increasing the timer to the amount of time it takes to shutdown, allows it to see the event. it's as if the timer event is overwriting the stop_main event.
LBPHacker #10
Posted 11 July 2014 - 06:03 PM
Your code seems to start a timer every time an event occurs. Well, don't do that; start a timer once and revive it only when you're sure it's dead. I'm not sure if that's the problem, but I once had a similar problem with timers and this is how I fixed it. Anyway, push the whole code to pastebin; seeing the big picture always helps.
Kizz #11
Posted 11 July 2014 - 06:05 PM
Here is the screen: http://pastebin.com/ibYi2e8q

Here is the server: http://pastebin.com/1RQgUyvG
Lyqyd #12
Posted 11 July 2014 - 06:06 PM
Your check server function may be eating the event, since whatever you're using to create a timeout has to yield. That's probably flushing the stop_main event, so your loop would never see it.
Kizz #13
Posted 11 July 2014 - 06:11 PM
Lyqyd, this is exactly what's happening, but I have no clue where to proceed.

Ultimately, chksvr uses a function called get:


function get(chan,tmout)--listener
if tmout==nil then
tmout=100000000000000000000000000000 --change to call event w/o timer
end
modem.open(chan) --opens port
chk=modem.isOpen(chan) --checks to make sure it is open
  if chk==true then --will set listener
  --mm = os.pullEvent("modem_message")
  timeout = os.startTimer(tmout)
  while true do
   event = {os.pullEvent()}
   if event[1] == "modem_message" then
    --print'Worked'
    --print(event[1].." "..event[2].." "..event[3].." "..event[4].." "..event[5].." "..event[6])
    msg=event[5]
    returnchan=event[4]
    chan=event[3]
    success=1
    break
    elseif event[1] == "timer" and event[2] == timeout then
    success=0
    break
   end
  end
end
if chk==false then
  --send error# to screen
  print('Error 100: Failed to Bind Port '..chan..'.')
end
end

Which is clearly using the event queue.
LBPHacker #14
Posted 11 July 2014 - 06:12 PM
screen, line 137: Why is the .pullEvent call a comment? That's what would give SCCH time to process the stop_main event. Or is it an edit we are not supposed to see?
Edited on 11 July 2014 - 04:12 PM
Kizz #15
Posted 11 July 2014 - 06:15 PM
WOOOWEHAHAH

Got it. 100% for sure this time!

Simply added


elseif event[1] == "stop_main" then
	return
   end

to my get function to catch the stop_main function.

Thanks guys!

LBP, I didn't see any purpose in having it pull the event there. I wasn't using the event, only producing it in that function. I tried it uncommented and commented and it made no difference to my code.
Edited on 11 July 2014 - 04:16 PM
LBPHacker #16
Posted 11 July 2014 - 06:23 PM
Yeah, right, my bad. The stop_main event is passed to both coroutines anyway. If not immediately, then in the next event cycle.
Kizz #17
Posted 11 July 2014 - 06:26 PM
:D/> Thanks, really glad I've joined the community. Learning a lot about Lua, ComputerCraft, and programming in general, and having fun too!

You two have been more than helpful, and I also will try and help anyone with questions that aren't above my ability, though few there may be.