local width, height = term.getSize()
local halfwidth = math.floor(width/2)
local window1 = window.create(term.current(), 1, 1, halfwidth, height)
local window2 = window.create(term.current(), halfwidth, 1, halfwidth, height)
parallel.waitForAll(
function()
--# I kow that this is only called once
term.redirect(window1)
shell.run("worm")
end,
function()
--# I kow that this is only called once
term.redirect(window2)
shell.run("paint image")
end
)
How can I manage to redirect the terminal in time so the right program can use it?
This is a read-only snapshot of the ComputerCraft forums,
taken in April 2020.
use parallel api to run multiple programs on one screen
Started by Jummit, 08 April 2018 - 05:54 PMPosted 08 April 2018 - 07:54 PM
I tried to run two programs at the same time, on a split screen, but I cant work out how to use term.redirect well. It always redirects it to one window for both programs. Here is the code:
Posted 08 April 2018 - 08:01 PM
If you write out the control flow of this program, it makes a little bit more sense what happens:
I'd recommend having a look at how multishell handles this, as well as the implementation of the parallel API. CraftOS's code isn't always the cleanest, but it's often a good starting point on how to implement features like this.
- Redirect to window1, run worm, worm yields
- Redirect to window2, run paint, paint yields
- Wait for an event
- Resume worm with this event, worm yields
- Resume paint with this event, paint yields,
- etc…
I'd recommend having a look at how multishell handles this, as well as the implementation of the parallel API. CraftOS's code isn't always the cleanest, but it's often a good starting point on how to implement features like this.
Posted 08 April 2018 - 08:07 PM
Yeah, I thought that would happen.If you write out the control flow of this program, it makes a little bit more sense what happens:Can you see what went wrong? We never redirected back into window1 when resuming after the first event, meaning both programs are stuck on window2.
- Redirect to window1, run worm, worm yields
- Redirect to window2, run paint, paint yields
- Wait for an event
- Resume worm with this event, worm yields
- Resume paint with this event, paint yields,
- etc…
I will do that, but I remembered from last time I looked at it that it looks complicated.Sadly this cannot be fixed with parallel - you'll need to write your own coroutine manager which handles redirecting the terminal.
I'd recommend having a look at how multishell handles this, as well as the implementation of the parallel API. CraftOS's code isn't always the cleanest, but it's often a good starting point on how to implement features like this.
Thanks btw for the quick reply! It was very helpful.
Posted 08 April 2018 - 08:23 PM
How would you/is the multishell api approaching this?
Posted 08 April 2018 - 08:26 PM
You're not meant to use it. You're meant to read it, understand it, and then implement something similar. If there's something specific you get stuck on, we can help, but otherwise we're just going to end up writing the whole program for you, which benefits nobody.How would you/is the multishell api approaching this?
Posted 08 April 2018 - 08:29 PM
I wanted to know the concept behind the multishell api, how to let programs run in their terminals, not how to use itYou're not meant to use it. You're meant to read it, understand it, and then implement something similar. If there's something specific you get stuck on, we can help, but otherwise we're just going to end up writing the whole program for you, which benefits nobody.How would you/is the multishell api approaching this?
Posted 08 April 2018 - 09:07 PM
Speaking of the API how does it handle function reentrancy? As in, can I trust my functions to be reentrent? Also does CC implement mutex locks and other synchronization tools?
Posted 08 April 2018 - 09:14 PM
Lua (and so CC) doesn't have preemptive multi-threading, but instead implements cooperative multithreading in the form of coroutines. This means only one thread* is ever running at once, and you have to explicitly change threads (through coroutine.yield). Consequently, you don't really need any fancy synchronisation schemes - you just need to be careful when and where you yield.Speaking of the API how does it handle function reentrancy? As in, can I trust my functions to be reentrent? Also does CC implement mutex locks and other synchronization tools?
*It's worth noting that PUC Lua doesn't even use threads at all - a yield is little more than a jump into the parent coroutine's function. LuaJ does use threads, but that's an implementation detail one does not need to worry about.
For instance, consider the following Lua code:
local count = 0
parallel.waitForAll(function()
while true do
os.pullEvent("test") -- Yields here
--# If Lua used preemptive multi-threading, this has the potential to be a race condition. However, + will not yield
--# so it's fine.
count = count + 1
end
end, function()
while true do
sleep(math.random(1, 5)) --# Yields here
count = count + 1
--# Allows the above function to continue (due to test event). Note it won't actually start running until we're processing the
--# "test" event, which may be several yields later.
os.queueEvent("test")
end
end)
Edited on 08 April 2018 - 07:21 PM
Posted 09 April 2018 - 08:15 AM
I know this isn't working in the slightest, but here is the code I have so far:
I get the typical too long without yielding error. What am I doing wrong here?
local width, height = term.getSize()
local halfwidth = math.floor(width/2)
local window1 = window.create(term.current(), 1, 1, halfwidth, height)
local window2 = window.create(term.current(), halfwidth, 1, halfwidth, height)
local worm = coroutine.create(function()
shell.run("worm")
end)
local paint = coroutine.create(function()
shell.run("paint test")
end)
while true do
term.redirect(window1)
coroutine.resume(worm)
term.redirect(window2)
coroutine.resume(paint)
end
I get the typical too long without yielding error. What am I doing wrong here?
Posted 09 April 2018 - 09:29 AM
So, why is this not working? Isn't sleep calling corouting.yield?
local co1 = coroutine.create(function()
for i = 1, 10 do
print("toast"..i)
sleep(0.5)
end
end)
local co2 = coroutine.create(function()
for i = 1, 10 do
print("test"..i)
sleep(0.5)
end
end)
local resume = function(co)
local status = coroutine.status(co)
if status == "normal" or status == "suspended" then
coroutine.resume(co)
print("resuming coroutine with status "..status)
else
print("not resuming coroutine with status "..status)
end
end
while true do
resume(co1)
resume(co2)
end
Edited on 09 April 2018 - 07:35 AM
Posted 09 April 2018 - 07:22 PM
Your coroutine manager doesn't yield, it needs to.
Edited on 09 April 2018 - 05:22 PM
Posted 09 April 2018 - 07:35 PM
Posted 09 April 2018 - 07:45 PM
I thought os.pullevent, sleep and some other CC functions where yielding? Also, with the new code that yields still does not work:Your coroutine manager doesn't yield, it needs to.
local co1 = coroutine.create(function()
for i = 1, 10 do
print("toast"..i)
sleep(0.5)
coroutine.yield()
end
end)
local co2 = coroutine.create(function()
for i = 1, 10 do
print("test"..i)
sleep(0.5)
coroutine.yield()
end
end)
local resume = function(co)
local status = coroutine.status(co)
if status == "normal" or status == "suspended" then
coroutine.resume(co)
end
end
while true do
resume(co1)
resume(co2)
end
Edited on 09 April 2018 - 05:46 PM
Posted 09 April 2018 - 08:11 PM
They do yield, to the code that called coroutine.resume. Which in this case is your infinate loop that never yields. See the diagram I posted above.
Posted 09 April 2018 - 08:23 PM
Is this how cc uses coroutines to manage events or is this how coroutines work in cc?
Posted 09 April 2018 - 08:54 PM
Both.
Posted 10 April 2018 - 08:27 AM
I read this, this, this and looked at the coroutine functions in the devdocs lua wiki. Now I want to die.
I managed to get it to work:
EDIT: this explains everything
Edited code:
I managed to get it to work:
local width, height = term.getSize()
local halfwidth = math.floor(width/2)
local window1 = window.create(term.current(), 1, 1, halfwidth, height)
local window2 = window.create(term.current(), halfwidth, 1, halfwidth, height)
local co1 = coroutine.create(function()
os.run(_G, "rom/programs/fun/worm.lua")
end)
local co2 = coroutine.create(function()
os.run(_G, "rom/programs/edit.lua", "test")
end)
while true do
term.redirect(window1)
coroutine.resume(co1, os.pullEventRaw())
term.redirect(window2)
coroutine.resume(co2, os.pullEventRaw())
end
But edit, paint and redirection always give the error 'loop in gettable'. What does this mean? The error comes from the shell api, but I don't know what happens in there.EDIT: this explains everything
Edited code:
local width, height = term.getSize()
local halfwidth = math.floor(width/2)
local window1 = window.create(term.current(), 1, 1, halfwidth, height)
local window2 = window.create(term.current(), halfwidth, 1, halfwidth, height)
local co1 = coroutine.create(function()
shell.run("rom/programs/fun/worm.lua")
end)
local co2 = coroutine.create(function()
shell.run("rom/programs/edit.lua", "test")
end)
while true do
local eventData = {os.pullEventRaw()}
term.redirect(window1)
coroutine.resume(co1, table.unpack(eventData))
term.redirect(window2)
coroutine.resume(co2, table.unpack(eventData))
end
Edited on 10 April 2018 - 08:15 AM
Posted 10 April 2018 - 11:20 AM
Which is all well and good at times, but not always. Like say the simplest of all situations. You have a large loop that processes a large number of things so you want to break in the middle to give others a chance to do some work. But you don't want some other program touching your data.Lua (and so CC) doesn't have preemptive multi-threading, but instead implements cooperative multithreading in the form of coroutines. This means only one thread* is ever running at once, and you have to explicitly change threads (through coroutine.yield). Consequently, you don't really need any fancy synchronisation schemes - you just need to be careful when and where you yield.
I am of course operating from the perspective of writing each program individually and not wanting to care about what else they will be running along side with as I do so.
Posted 10 April 2018 - 07:46 PM
Like say the simplest of all situations. You have a large loop that processes a large number of things so you want to break in the middle to give others a chance to do some work. But you don't want some other program touching your data.
Use local variables?