Try   HackMD

Runtime agnostic lib

  • Character: Barbara

  • Barbara and Alan work at AmoolgeSoft where many teams are switching from Java to Rust. These teams have many different use cases and various adoption stories.

    • Some teams are happy users of tokio, others happy users of async-std, and others still are using custom runtimes for highly specialized use cases.
  • Barbara is tasked with writing a library for a custom protocol (SLOW) only in use at AmoogleSoft and enlists the help of Alan in doing so.

    • Alan is already aware that not all libraries in Rust work with all runtimes. TODO: link to HTTP story
  • Alan and Barbara start by writing a parser which works on std::io::Read and get their tests working with Strings. After this they contemplate the question of how to accept a TCP connection.

  • Alan asks Barbara what is the async equivalent is of std::io::Read, and Barbara sighs and says that there isn't one.

    • Barbara brings up tokio's and the futures crate's versions of AsyncRead. Barbara decides not to talk about AsyncBufRead for now.
  • Barbara and Alan decide to use the future's AsyncRead for no other reason other than it is not tied to a specific runtime. Barbara tells Alan not to worry, they can translate between the two.

    • With some effort (TODO: maybe link a story about Pin?) they convert their parser to using AsyncRead
  • Alan, excited about the progress they've made, starts working on hooking this up to actual TCP streams.

    • Alan looks at async-std and tokio and notices their interfaces for TCP are quite different. Alan waits for Barbara to save the day.
  • Barbara helps abstract over TCP listener and TCP stream (TODO: code example)

    • One big issue is that tokio uses AsyncRead from their own crate and not the one from futures
  • Spawning tasks - there's no agnostic way to do that

    • Unsure how to do this, they do some searching and find the agnostik crate.
    • Reject it because this only supports N number of runtimes and their custom runtime is obviously not one of them.
    • Gives them the idea to provide a trait for specifying how to spawn tasks on the runtime.
      • This has disadvantage of working against orphan rules meaning that either they have implement the trait for all known runtimes (defeating the purpose of the exercise) or force the user to use new types.
    • They punt on this question by implementing the trait for each of the known runtimes. They're disappointed that this means their library actually isn't runtime agnostic.
  • They need timers

    • They could add this to the existing trait they use for spawning, but they find a runtime agnostic library.
    • It works but is pretty heavy in that it spawns an OS thread (from a pool) every time they want to sleep.
    • They become sadder
  • They need channels - after long searches and discussions on help channels, they learn about runtime agnostic implementations:

    • Agnostic crates exist: e.g. async-channel, futures-rs channels, async-std/tokio's feature flags
    • They pick one and it seems to work fine
  • They get things working but it was hard. Some find the APIs harder to use than their runtime non-agnostic libs

Morals

  • People have to roll their own implementations which can lead to often subtle differences between runtimes
    • Example TCPListeners in async-std and tokio
  • Orphan rules and no standard traits guarantee that a truly agnostic library is not possible
  • Takes way more time than writing synchronous protocols
  • Its hard
  • Leads to poorer APIs sometimes (both in ease of use and performance)
  • More thought is needed to create these

FAQ

  • How do characters respond?
    • Alan, Grace, Niklaus: overwhelmed
    • Barbara: overwhelmed but can sort it out
  • What are the downside of using runtime agnostic crates?
    • Some things can be implemented very efficiently in a runtime agnostic way
    • But even then you can't integrate deeply into the runtime; see e.g. tokio's pre-emption strategy, which relies on deep integration with the runtime
  • What other runtime utilities are generally needed?
    • async-locks (link to story)