MightyPirates / OpenComputers

Home of the OpenComputers mod for Minecraft.
https://oc.cil.li
Other
1.59k stars 433 forks source link

RT mode #2269

Closed SoniEx2 closed 7 years ago

SoniEx2 commented 7 years ago

So I came up with this idea for a real-time mode:

A real-time mode computer is a computer that runs in a separate thread pool and gets notified every game tick.

If such computer fails to yield within a game tick, it gets killed.

If the time spent in the thread pool is greater than a game tick (e.g. you have 5 computers, 4 threads, and each computer yielded after roughly 0.04 seconds, you get the following per-thread times: T1: 0.08 T2: 0.04 T3: 0.04 T4: 0.04), then the slowest computer gets killed.

If a component function call might take longer than a game tick to complete, it returns nil, "RT_TIME". Unlike in the 2 situations above, this is the only way a program can gracefully handle RT timeouts.

The thread pool should have "logarithmic" growth, as defined by Integer.highestOneBit(int), but may be capped by the server admin.

xarses commented 7 years ago

I can't envision a implementation that would be able to receive each game tick, be outside the main thread, and properly killed if it's over budget. Either they are fully in the main thread, and will delay it, or they are not.

Please see https://github.com/MightyPirates/OpenComputers/pull/2168 which is probably as close to what can be implemented to support precise per-tick execution.

What is the use case(s) here that would require this kind of precision?

SoniEx2 commented 7 years ago

Playing music.

Also, it's pretty easy to receive each game tick, be outside the main thread, and properly killed. Debug hooks and concurrent queues help a lot with that.

On each tick, you add a tick to the queue. Debug hooks assert that the queue is empty. When a computer fully yields you can block the queue from the java/scala side and push the new tick event.

This doesn't require it to be admin-controlled.

MajorGeneralRelativity commented 7 years ago

I also think that you could achieve the same functionality with #2168 which is also designed to allow precise computer timing control.

SoniEx2 commented 7 years ago

This doesn't require it to be admin-controlled. This also isn't synchronous. I don't need synchronous since note playback is async anyway.

MajorGeneralRelativity commented 7 years ago

Fair point. The technical details of stuff like this eludes my skill level for the time being, so I'll leave this to someone else then.

NoX-programer commented 7 years ago

Playing music using OC (CC, whatever) computer is an interesting task. It should be done smart because we need to hear music on the client side. http://wiki.vex.tty.sh/wiki:computronics already adds some stuff to play music, so look into it.

SoniEx2 commented 7 years ago

How about queues and futures/promises?

local future = computer.queue(someModFunction, args)

e.g.

local future1 = computer.queue(noteBlock.playNote, "harp", 12, 1.0)
local future2 = computer.queue(noteBlock.playNote, "harp", 10, 1.0)
local ok, err = future1.wait() -- yields/blocks until future1 is complete. results same as pcall
local ok2, err2 = future2.wait() -- shouldn't wait since both operations should be done in the same tileentity tick, before the computer thread is resumed
assert(ok, err) assert(ok2, err2) -- run the asserts on the calling code (i.e. not in a callback) so the backtrace can tell us what went wrong

(Maybe we should use a queue object, and first you add stuff to the queue, then you ask it to be processed. That way you can guarantee the calls get done in the order you want them.)

If a mod function attempts to call Lua code, an error should be raised.

The queue should be processed in order. If we have queue objects, they should have a "mode" tag, which can be set to either continue-on-error or stop-on-error.

payonel commented 7 years ago

We have threads in OpenOS 1.6.4

local thread = require("thread")
local t1 = thread.create(function() noteBlock.playNote("harp", 12, 1.0) end)
local t2 = thread.create(function() noteBlock.playNote("harp", 10, 1.0) end)
thread.waitForAll({t1, t2})

or

local thread = require("thread")
local t1 = thread.create(noteBlock.playNote, "harp", 12, 1.0)
local t2 = thread.create(noteBlock.playNote, "harp", 10, 1.0)
thread.waitForAll({t1, t2})

or

local thread = require("thread")
thread.create(function()
  thread.create(function() noteBlock.playNote("harp", 12, 1.0) end)
  thread.create(function() noteBlock.playNote("harp", 10, 1.0) end)
end):join()
SoniEx2 commented 7 years ago

Threads don't guarantee you'll be able to run a set of synchronous mode operations (those that usually block for a full game tick each) in-order in the same game tick tho, do they?

payonel commented 7 years ago

Concurrently, no. But we're never adding concurrency to Lua archs. In parallel? yes, as long as your threads yield or pull

SoniEx2 commented 7 years ago

How about in order, one after the other, but all within the same game tick? And with no access from the Lua side except after complete queue exhaustion, so that the Lua side can't increase tile entity tick time.

Think a for (QueueItem queueItem : queue) { queueItem.process(); } inside the TileEntity update() method.

payonel commented 7 years ago

threads start when created, but resume in unspecified order it all can happen within a single game tick if you haven't used up your thread execution time we're not adding execution threads outside of the Lua sandbox and you should know, we are not planning to add RT mode to OC

SoniEx2 commented 7 years ago

But I came up with a way to do it that isn't a security issue. Something everyone can benefit from.

payonel commented 7 years ago

RT mode is an interesting feature that has been asked in various different ways but we've decided we're not going to make this change.

xarses commented 7 years ago

Not to beat a dead horse beats the dead horse. Would it be tenable to have a thread request a deadline, and if it's not met raise some kind of error when it's not met (also terminating the thread exec) so that things that have to be done in time are, or at least stop running?