Venice

Swift License Slack Travis Codecov Codebeat Documentation

Venice provides structured concurrency and CSP for Swift.

Features

  • Coroutines
  • Coroutine cancelation
  • Coroutine groups
  • Channels
  • Receive-only channels
  • Send-only channels
  • File descriptor polling

Venice wraps a fork of the C library libdill.

Installation

  • Add Venice to your Package.swift
import PackageDescription

let package = Package(
    dependencies: [
        .Package(url: "https://github.com/Zewo/Venice.git", majorVersion: 0, minor: 18)
    ]
)

Structured Concurrency

Structured concurrency means that lifetimes of concurrent functions are cleanly nested. If coroutine foo launches coroutine bar, then bar must finish before foo finishes.

This is not structured concurrency:

not-structured-concurrency

This is structured concurrency:

structured-concurrency

The goal of structured concurrency is to guarantee encapsulation. If the main function calls foo, which in turn launches bar in a concurrent fashion, main will be guaranteed that once foo has finished, there will be no leftover functions still running in the background.

What you end up with is a tree of coroutines rooted in the main function. This tree spreads out towards the smallest worker functions, and you may think of this as a generalization of the call stack — a call tree, if you will. In it, you can walk from any particular function towards the root until you reach the main function:

call-tree

Venice implements structured concurrency by allowing you to cancel a running coroutine.

let coroutine = try Coroutine {
    let resource = malloc(1000)

    defer {
        free(resource)
    }

    while true {
        try Coroutine.wakeUp(100.milliseconds.fromNow())
        print(".")
    }
}

try Coroutine.wakeUp(1.second.fromNow())
coroutine.cancel()

When a coroutine is being canceled all coroutine-blocking calls will start to throw VeniceError.canceledCoroutine. On one hand, this forces the function to finish quickly (there’s not much you can do without coroutine-blocking functions); on the other hand, it provides an opportunity for cleanup.

In the example above, when coroutine.cancel is called the call to Coroutine.wakeUp inside the coroutine will throw VeniceError.canceledCoroutine and then the defer statement will run, thus releasing the memory allocated for resource.

Threads

You can use Venice in multi-threaded programs. However, individual threads are strictly separated. You may think of each thread as a separate process.

In particular, a coroutine created in a thread will be executed in that same thread, and it will never migrate to a different one.

In a similar manner, a handle, such as a channel or a coroutine handle, created in one thread cannot be used in a different thread.

License

This project is released under the MIT license. See LICENSE for details.