All Projects → status-im → Nim Chronos

status-im / Nim Chronos

Licence: other
Chronos - An efficient library for asynchronous programming

Programming Languages

nim
578 projects

Projects that are alternatives of or similar to Nim Chronos

Unityfx.async
Asynchronous operations (promises) for Unity3d.
Stars: ✭ 143 (+5.15%)
Mutual labels:  async, async-await, asynchronous-programming
Tascalate Async Await
Async / Await asynchronous programming model for Java similar to the functionality available in C# 5. The implementation is based on continuations for Java (see my other projects).
Stars: ✭ 60 (-55.88%)
Mutual labels:  async, async-await, asynchronous-programming
Cppcoro
A library of C++ coroutine abstractions for the coroutines TS
Stars: ✭ 2,118 (+1457.35%)
Mutual labels:  async, async-await, asynchronous-programming
P Map
Map over promises concurrently
Stars: ✭ 639 (+369.85%)
Mutual labels:  async, async-await
Fasy
FP iterators that are both eager and asynchronous
Stars: ✭ 488 (+258.82%)
Mutual labels:  async, asynchronous-programming
Posterus
Composable async primitives with cancelation, control over scheduling, and coroutines. Superior replacement for JS Promises.
Stars: ✭ 536 (+294.12%)
Mutual labels:  async, async-await
Promise Fun
Promise packages, patterns, chat, and tutorials
Stars: ✭ 3,779 (+2678.68%)
Mutual labels:  async, async-await
Uvloop
Ultra fast asyncio event loop.
Stars: ✭ 8,246 (+5963.24%)
Mutual labels:  async, async-await
Swiftcoroutine
Swift coroutines for iOS, macOS and Linux.
Stars: ✭ 690 (+407.35%)
Mutual labels:  async, async-await
Preact Cli Plugin Async
Preact CLI plugin that adds converts async/await to Promises.
Stars: ✭ 44 (-67.65%)
Mutual labels:  async, async-await
Ea Async
EA Async implements async-await methods in the JVM.
Stars: ✭ 1,085 (+697.79%)
Mutual labels:  async, async-await
Rubico
[a]synchronous functional programming
Stars: ✭ 133 (-2.21%)
Mutual labels:  async, async-await
Trio
Trio – a friendly Python library for async concurrency and I/O
Stars: ✭ 4,404 (+3138.24%)
Mutual labels:  async, async-await
Concurrencpp
Modern concurrency for C++. Tasks, executors, timers and C++20 coroutines to rule them all
Stars: ✭ 340 (+150%)
Mutual labels:  async-await, asynchronous-programming
Chillout
Reduce CPU usage by non-blocking async loop and psychologically speed up in JavaScript
Stars: ✭ 565 (+315.44%)
Mutual labels:  async, async-await
P Iteration
Utilities that make array iteration easy when using async/await or Promises
Stars: ✭ 337 (+147.79%)
Mutual labels:  async, async-await
Then
🎬 Tame async code with battle-tested promises
Stars: ✭ 908 (+567.65%)
Mutual labels:  async, async-await
Aiormq
Pure python AMQP 0.9.1 asynchronous client library
Stars: ✭ 112 (-17.65%)
Mutual labels:  async, async-await
Promesa
A promise library for Clojure(Script)
Stars: ✭ 277 (+103.68%)
Mutual labels:  async-await, asynchronous-programming
Sqlalchemy aio
Asyncio strategy for SQLAlchemy.
Stars: ✭ 299 (+119.85%)
Mutual labels:  async, async-await

Chronos - An efficient library for asynchronous programming

Github action Windows build status (AppVeyor) License: Apache License: MIT Stability: experimental

Introduction

Chronos is an efficient async/await framework for Nim. Features include:

  • Efficient dispatch pipeline for asynchronous execution
  • HTTP server with SSL/TLS support out of the box (no OpenSSL needed)
  • Cancellation support
  • Synchronization primitivies like queues, events and locks
  • FIFO processing order of dispatch queue

Installation

You can use Nim's official package manager Nimble to install Chronos:

nimble install https://github.com/status-im/nim-chronos.git

or add a dependency to your .nimble file:

requires "chronos"

Projects using chronos

  • libp2p - Peer-to-Peer networking stack implemented in many languages
  • Looper - Web framework
  • 2DeFi - Decentralised file system

chronos is available in the Nim Playground

Submit a PR to add yours!

Documentation

Concepts

Chronos implements the async/await paradigm in a self-contained library, using macros, with no specific helpers from the compiler.

Our event loop is called a "dispatcher" and a single instance per thread is created, as soon as one is needed.

To trigger a dispatcher's processing step, we need to call poll() - either directly or through a wrapper like runForever() or waitFor(). This step handles any file descriptors, timers and callbacks that are ready to be processed.

Future objects encapsulate the result of an async procedure, upon successful completion, and a list of callbacks to be scheduled after any type of completion - be that success, failure or cancellation.

(These explicit callbacks are rarely used outside Chronos, being replaced by implicit ones generated by async procedure execution and await chaining.)

Async procedures (those using the {.async.} pragma) return Future objects.

Inside an async procedure, you can await the future returned by another async procedure. At this point, control will be handled to the event loop until that future is completed.

Future completion is tested with Future.finished() and is defined as success, failure or cancellation. This means that a future is either pending or completed.

To differentiate between completion states, we have Future.failed() and Future.cancelled().

Dispatcher

You can run the "dispatcher" event loop forever, with runForever() which is defined as:

proc runForever*() =
  while true:
    poll()

You can also run it until a certain future is completed, with waitFor() which will also call Future.read() on it:

proc p(): Future[int] {.async.} =
  await sleepAsync(100.milliseconds)
  return 1

echo waitFor p() # prints "1"

waitFor() is defined like this:

proc waitFor*[T](fut: Future[T]): T =
  while not(fut.finished()):
    poll()
  return fut.read()

Async procedures and methods

The {.async.} pragma will transform a procedure (or a method) returning a specialised Future type into a closure iterator. If there is no return type specified, a Future[void] is returned.

proc p() {.async.} =
  await sleepAsync(100.milliseconds)

echo p().type # prints "Future[system.void]"

Whenever await is encountered inside an async procedure, control is passed back to the dispatcher for as many steps as it's necessary for the awaited future to complete successfully, fail or be cancelled. await calls the equivalent of Future.read() on the completed future and returns the encapsulated value.

proc p1() {.async.} =
  await sleepAsync(1.seconds)

proc p2() {.async.} =
  await sleepAsync(1.seconds)

proc p3() {.async.} =
  let
    fut1 = p1()
    fut2 = p2()
  # Just by executing the async procs, both resulting futures entered the
  # dispatcher's queue and their "clocks" started ticking.
  await fut1
  await fut2
  # Only one second passed while awaiting them both, not two.

waitFor p3()

Don't let await's behaviour of giving back control to the dispatcher surprise you. If an async procedure modifies global state, and you can't predict when it will start executing, the only way to avoid that state changing underneath your feet, in a certain section, is to not use await in it.

Error handling

Exceptions inheriting from CatchableError are caught by hidden try blocks and placed in the Future.error field, changing the future's status to Failed.

When a future is awaited, that exception is re-raised, only to be caught again by a hidden try block in the calling async procedure. That's how these exceptions move up the async chain.

A failed future's callbacks will still be scheduled, but it's not possible to resume execution from the point an exception was raised.

proc p1() {.async.} =
  await sleepAsync(1.seconds)
  raise newException(ValueError, "ValueError inherits from CatchableError")

proc p2() {.async.} =
  await sleepAsync(1.seconds)

proc p3() {.async.} =
  let
    fut1 = p1()
    fut2 = p2()
  await fut1
  echo "unreachable code here"
  await fut2

# `waitFor()` would call `Future.read()` unconditionally, which would raise the
# exception in `Future.error`.
let fut3 = p3()
while not(fut3.finished()):
  poll()

echo "fut3.state = ", fut3.state # "Failed"
if fut3.failed():
  echo "p3() failed: ", fut3.error.name, ": ", fut3.error.msg
  # prints "p3() failed: ValueError: ValueError inherits from CatchableError"

You can put the await in a try block, to deal with that exception sooner:

proc p3() {.async.} =
  let
    fut1 = p1()
    fut2 = p2()
  try:
    await fut1
  except:
    echo "p1() failed: ", fut1.error.name, ": ", fut1.error.msg
  echo "reachable code here"
  await fut2

Exceptions inheriting from Defect are treated differently, being raised directly. Don't try to catch them coming out of poll(), because this would leave behind some zombie futures.

TODO

  • Pipe/Subprocess Transports.
  • Multithreading Stream/Datagram servers

Contributing

When submitting pull requests, please add test cases for any new features or fixes and make sure nimble test is still able to execute the entire test suite successfully.

chronos follows the Status Nim Style Guide.

Other resources

License

Licensed and distributed under either of

or

at your option. These files may not be copied, modified, or distributed except according to those terms.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].