diff --git a/docs/design.md b/docs/design.md index 767514af0..a5910340a 100644 --- a/docs/design.md +++ b/docs/design.md @@ -276,9 +276,7 @@ So starting with `0.4`, the synchronous I/O was replaced with asynchronous I/O. * Inherently multi-threaded via Tokio's executor. * Supports timers and other kinds of asynchronous operation. -The penalty for this is that asynchronous programming is _hard_. It's hard even in languages like JavaScript where things like lifetimes and borrowing don't have to be thought about. In Rust, async means reference counting things, frequently having to map the output of one future into another kind and so forth. - -In addition Tokio has been the cause of its own problems. The timers in tokio-timer 0.1 were broken for sub-100ms timers and not finegrained but this was resolved in 0.2. Issues with closing streams after splitting a stream into reader / writer portions is also a problem that was only recently solved. +The penalty for this is that asynchronous programming is _hard_ even when the language supplies constructs to support it. In the new async world a session is a state machine: diff --git a/docs/tokio.md b/docs/tokio.md index e69de29bb..1ebe66755 100644 --- a/docs/tokio.md +++ b/docs/tokio.md @@ -0,0 +1,63 @@ +# Into + +OPC UA for Rust uses Tokio for network I/O, timers and for asynchronous scheduling. Tokio +is basically a scheduler and library used in conjunction with Rust primitives `async` and `await` +for asynchronous execution of code.. + +Within OPC UA for Rust, async is used for. + +* Listening for connections +* Making connections +* Handshake +* Read / write portions of messaging +* Timeouts +* Side channel commands to abort connections + +Despite that, the implementation is also kind of clunky in ways and future async work should +involve fixing or mitigating that clunkiness. + +# Synchronous client API + +The client side API is all synchronous externally and async internally. In the future +it would be nice to offer an async and a synchronous API without massively breaking +the existing API. + +Breakage is very probable though because the current code uses read-write locks on the session +for synchronous calls which would not be conduicive to async. + +1. Remove `Arc>` if possible. e.g. perhaps Session becomes a cloneable facade with internal locks if necessary but make the struct callable from outside without obtaining any lock. +2. Clean up innards of existing sync - async bridge to make use of Tokio, i.e. replace thread::sleep code + with async blocks using async timers. +3. ??? +4. Asynchronous / Synchronous interfaces + + +# Synchronous server API + +For the most part it doesn't matter that the server is synchronous because most servers are going +to be set-it-and-forget-it deals. Where it might have an impact is on historical read / update +activities, or setter/getters on variables. Basically there may be situations where the server +calls out to the implementor and it would nice if those call outs were asynchronous in some manner. + +# Too many threads + +Server and client are spawning too many threads for different aspects of their runtime + +* Client spawns TWO tokio executors + * Session has a thread::spawn for an executor + * TcpTransport has its own thread::spawn and executor. This executor can be single or multi threaded + depending on configuration. + +In theory Client should be able to execute from a single thread assuming Tokio executor was invoked from +main and async API was used. Even in the synchronous case, then 2 threads should be possible - the +synchronous main thread and the session/transport tokio executor thread. + +Server side thread use isn't quite so important but it would be nice if thread use +could be minimized. + +# Clunky internal mechanics + +There are quit flags, states, timers and too much polling going on. Some of this could be simplified. + +Ideally it should be possible for a task to be triggered by a state change such that it can loop but not poll on a timer, but on actual change of date it is interested in. +