MightyPirates / OpenComputers

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

Threads/Asynchronous synchronous calls #2475

Closed SoniEx2 closed 7 years ago

SoniEx2 commented 7 years ago

Can we get async programming in the form of threads?

Threads are just coroutines powered by callbacks.

Each synchronous Java function (e.g. anything that interfaces with a tile entity) gets a callback mechanism, and that callback mechanism resumes a "system-wide coroutine". The coroutine code can be done in pure Lua, altho the callback system needs to be done in JVM land.

A global event loop processes callbacks, while the synchronous Java functions add the calls themselves to a queue. The calls are done as soon as possible but the callbacks are only called when the event loop takes control. The event loop takes control when the computer yields.

This mechanism should also work for disk I/O and network I/O, so code can still run while other code waits for I/O to finish. (Yes, I know, those aren't synchronized with the main game thread, but they could still be more async-y. Also, it would free up Lua threads, such that 4 Lua computers doing disk I/O wouldn't block all others.)

TL;DR: Just port LibUV/Luvit to the JVM.

payonel commented 7 years ago

disk/network I/O does not yield a lua coroutine. so there would be no preemptive thread switching during I/O. The network api is already non blocking, and we already have signals to support async network data.

The only blocking call is coroutine.yield out of the machine state (such as via computer.pullSignal, or coroutine.yield on a top level coroutine). These calls can already be run concurrently using OpenOS threads. See http://ocdoc.cil.li/api:thread

The event loop takes control when the computer yields

This is no different than OpenOS' current event dispatch engine. Computer signals and timers and threads are resumed, callbacks are made, concurrently and when the "main" init thread yields

I don't see any specific feature request here that we do not already handle. Feel free to provide sample lua code that currently either does not work but would with this feature, or would work better.

SoniEx2 commented 7 years ago

Since disk I/O is blocking as you say, then during disk I/O the Lua worker thread, which there are 4 of, is stalled.

By using async, callback-based disk I/O and a synchronous, coroutine-based API on top, you'd be able to unstall those worker threads, allowing more OC computers to run at the same time.

For how to convert async callback APIs into coroutine-based "synchronous" APIs, take a look at https://github.com/luvit/lit/blob/master/deps/coro-channel.lua

Other benefits include being able to call multiple STT (server tick thread) methods from the same computer, since they'd all be async calls and use callbacks for completion (as opposed to stalling the whole OC computer for a whole tick).

magik6k commented 7 years ago

When Disk IO is done, the computers yield giving time to other computers, the blocking is async under the hood. Async calls might speed things up, but IMO optimizing things in Lua should be a the way to go. You can use unmanaged drives for fast data access, yes it's more difficult, that's why they are faster.

You can also bump call budgets in config if you feel your computers are too slow and you are unable/unwilling to optimize your programs.

SoniEx2 commented 7 years ago

But I need to be able to play multiple noteblock notes using a "synchronous" (read: blocks the OC computer until next tick) method.

SoniEx2 commented 7 years ago

Real computer:

  1. Ask drive for data
  2. Do other tasks
  3. On interrupt, finish I/O task

OC:

  1. Ask drive for data
  2. Block
  3. Resume

Any chance you can get that fixed? Or is it intended that I need to build 100 OC computers and give each of them a "synchronous" main thread task to get anything resembling a real computer?

magik6k commented 7 years ago

Have you considered other options like syncing multiple computers/uCs / using bundled redstone?

OC is using blocking calls because it:

SoniEx2 commented 7 years ago

Coroutine wrappers are just as simple on the Lua side.

Yeah I'll just get a NAS, network attached server. all blocking calls go to the NAS, and I wait for the NAS to give me an interrupt.

skyem123 commented 7 years ago

Having more stuff be async would be interesting but probably counterproductive as it would add more overhead. I feel like it would be a waste of effort on both OC code and user code.

SoniEx2 commented 7 years ago

But it would let server-thread noteblocks work with a single computer playing them. Also, this is the closest to real-time mode you can get, without going real-time mode.