Venice - Coroutines, structured concurrency and CSP for Swift on macOS and Linux.

Overview

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

Before using Venice you need to install our libdill fork. Follow the instruction for your operating system.

macOS

On macOS install libdill using brew.

brew install zewo/tap/libdill

Linux

On Linux we have to add our apt source first. You only need to run this command once in a lifetime. You don't need to run it again if you already have.

echo "deb [trusted=yes] http://apt.zewo.io ./" | sudo tee -a /etc/apt/sources.list
sudo apt-get update

Now just install the libdill apt package.

sudo apt-get install libdill

Add Venice to Package.swift

After installing libdill just add Venice as a dependency in your Package.swift file.

import PackageDescription

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

Test Coverage

Coverage Sunburst

The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is represented by the number of statements and the coverage, respectively.

Documentation

You can check the Venice API reference for more in-depth documentation.

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.

Comments
  • UDP socket support?

    UDP socket support?

    I noticed that your readme doesn't have UDP socket support checked off. Is this something you're planning on adding? Definitely interested in utilizing this project, but UDP is a requirement for us.

    Thanks!

    opened by justmark 7
  • Please Provide More Documentation and example  of Venice

    Please Provide More Documentation and example of Venice

    thanks Venice u save my lot of time for writing code from scratch but there is not very much documentation and example of Venice

    Documentation like what what class do ,what method do what about it what kind of parameter it takes what it return and a simple working example of it

    it will be very helpful if you provide more documentation of Venice and Example of it

    Thanks in advance :+1:

    opened by indrajitsinh 5
  • Update README examples to 09-06

    Update README examples to 09-06

    Following @Danappelxx suggestion, here are the updated docs. I heading to the GitBook now :)

    As a side note, I updated .swift-version to 09-06.

    Cheers

    opened by bosr 4
  • Ticker should emit Double timestamps instead of Void

    Ticker should emit Double timestamps instead of Void

    Hi,

    Some examples the Venice Docs show that a Ticker emits timestamps on its channel (for time in Ticker().channel { … }). But in the current 0.12.0 Venice, a Ticker emits on Channel<Void>. I could not find a commit regarding this change.

    As I think it’s nice to have, I’d like to re-add this : a Ticker will emit timestamp (based on C7.Time.now()) on its Channel<UInt64>.

    Do you agree?

    opened by bosr 4
  • Substitute global variables with Int extensions

    Substitute global variables with Int extensions

    Right now second, millisecond, minute and hour are just hardcoded Int64 values. As alternative, I propose using Int extensions to make code slightly more readable and safe:

    nap(1.second)
    after(5.seconds) {
        // Do something
    }
    

    It can be achieved this way:

    extension Int {
        var millisecond: Int64 {
            return Int64(self * 1)
        }
        var milliseconds: Int64 {
            return millisecond
        }
        var second: Int64 {
            return Int64(self * 1000)
        }
        var seconds: Int64 {
            return second
        }
        // and so on
    }
    

    Or nicer — with protocol:

    public protocol IntervalConvertible {
        var millisecond: Int64 { get }
        var second: Int64 { get }
        // and so on
    }
    
    extension IntervalConvertible {
        public var milliseconds: Int64 {
            return millisecond
        }
        public var second: Int64 {
            return millisecond * 1000
        }
        public var seconds: Int64 {
            return second
        }
        // and so on
    }
    
    extension Int: IntervalConvertible {
        public var millisecond: Int64 {
            return Int64(self * 1)
        }
    }
    

    And even now can has it’s alternative:

    extension Int64 {
        public var fromNow: Int64 {
            return Clibvenice.now() + self
        }
    }
    

    So one will be able to write:

    let timer1 = Timer(deadline: now + 2 * second)
    let timer2 = Timer(deadline: 2.seconds.fromNow)
    
    opened by dreymonde 4
  • Fibonacci sequence

    Fibonacci sequence

    Great work! I just wanted to point out that the output from the Fibonacci example shows 0,1,2,4,8,16... (2^N) instead of 1,1,2,3,5,8... as one would expect.

    opened by KennethLj 4
  • Added better code coverage for FileDescriptor tests

    Added better code coverage for FileDescriptor tests

    Summary

    • Added extra test scenarios for better code coverage for FileDescriptor class.
    • Changed a small error checking in init that was unnecessary since the file descriptor was already tested few lines before.
    opened by ronflima 3
  • error: extra argument in call

    error: extra argument in call

    Looks like there's an issue with Venice 0.1.1 and compatibility with libvenice 0.1.0. I checked the brew formula and didn't see an update. Here's output from my console.

    Compiling Swift Module 'Venice' (21 sources)
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Channel.swift:47:48: error: extra argument in call
            self.channel = mill_chmake(bufferSize, "Channel init")
                                                   ^~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Channel.swift:51:31: error: extra argument in call
            mill_chclose(channel, "Channel deinit")
                                  ^~~~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Channel.swift:69:34: error: extra argument in call
                mill_chdone(channel, "Channel close")
                                     ^~~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Channel.swift:77:31: error: extra argument in call
                mill_chs(channel, "Channel send")
                                  ^~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Channel.swift:94:27: error: extra argument in call
            mill_chr(channel, "Channel receive")
                              ^~~~~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Co.swift:45:8: error: extra argument in call
        }, "co")
           ^~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Co.swift:53:8: error: extra argument in call
        }, "co")
           ^~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Co.swift:66:21: error: missing argument for parameter #3 in call
        return goprepare(Int32(stackCount), stackSize)
                        ^
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Co.swift:71:33: error: extra argument in call
        mill_msleep(now + duration, "nap")
                                    ^~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Co.swift:76:27: error: extra argument in call
        mill_msleep(deadline, "wakeUp")
                              ^~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Co.swift:81:16: error: argument passed to call that takes no arguments
        mill_yield("yield")
                  ~^~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Co.swift:86:12: error: use of unresolved identifier 'mfork'
        return mfork()
               ^~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Co.swift:91:16: error: use of unresolved identifier 'mill_number_of_cores'
        return Int(mill_number_of_cores())
                   ^~~~~~~~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/FallibleChannel.swift:66:48: error: extra argument in call
            self.channel = mill_chmake(bufferSize, "FallibleChannel init")
                                                   ^~~~~~~~~~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/FallibleChannel.swift:70:31: error: extra argument in call
            mill_chclose(channel, "FallibleChannel deinit")
                                  ^~~~~~~~~~~~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/FallibleChannel.swift:88:34: error: extra argument in call
                mill_chdone(channel, "FallibleChannel close")
                                     ^~~~~~~~~~~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/FallibleChannel.swift:96:31: error: extra argument in call
                mill_chs(channel, "FallibleChannel sendResult")
                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/FallibleChannel.swift:105:31: error: extra argument in call
                mill_chs(channel, "FallibleChannel send")
                                  ^~~~~~~~~~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/FallibleChannel.swift:123:31: error: extra argument in call
                mill_chs(channel, "FallibleChannel send")
                                  ^~~~~~~~~~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/FallibleChannel.swift:141:27: error: extra argument in call
            mill_chr(channel, "FallibleChannel receive")
                              ^~~~~~~~~~~~~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/FallibleChannel.swift:157:27: error: extra argument in call
            mill_chr(channel, "FallibleChannel receiveResult")
                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Poller.swift:53:79: error: extra argument in call
        let event = mill_fdwait(fileDescriptor, Int32(events.rawValue), deadline, "pollFileDescriptor")
                                                                                  ^~~~~~~~~~~~~~~~~~~~
    /Users/ssoper/workspace/Runabout/Packages/Venice-0.1.1/Source/Select.swift:340:22: error: argument passed to call that takes no arguments
        mill_choose_init("select")
                        ~^~~~~~~~~
    <unknown>:0: error: build had 1 command failures
    swift-build: exit(1): ["/Library/Developer/Toolchains/swift-2.2-SNAPSHOT-2015-12-18-a.xctoolchain/usr/bin/swift-build-tool", "-f", "/Users/ssoper/workspace/Runabout/.build/debug/Venice.o/llbuild.yaml"]
    
    opened by ssoper 3
  • Fix a type in

    Fix a type in "Example 14 - Rate Limiting" in README.

    Problem

    The burst example in README got "Binary operator '<-' cannot be applied to operands of type 'Channel<Int>' and 'Int64'" because the type mismatch of the Int channel and now and time generated from Ticker channel as Int64.

    let burstyLimiter = Channel<Int>(bufferSize: 3)
    
    for _ in 0 ..< 3 {
        burstyLimiter <- now // Compiler error here. now is Int64.
    }
    
    co {
        for time in Ticker(period: 200 * millisecond).channel {
            burstyLimiter <- time // Compiler error here. time is Int64.
        }
    }
    

    Fix

    Changed the type of the channel to Int64.

    Environment

    • Venice 0.9
    • Xcode 7.1.1
    • OS X 10.11.1
    opened by yoichitgy 2
  • Better way?

    Better way?

    Is there a better way to a achieve the following? Perhaps will only one channel? But then, I need to close it to know when all coroutines have finished. Not sure how to improve this.

    import Venice
    
    var numbers: [Int] = []
    
    for i in 2...100 {
        numbers.append(i)
    }
    
    var primes: [Channel<Int>] = []
    
    for number in numbers {
        let prime = Channel<Int>()
        primes.append(prime)
    
        co {
            for i in 2..<number {
                if number % i == 0 {
                    prime.close()
                    return
                }
            }
    
            prime <- number
            prime.close()
        }
    }
    
    for prime in primes.flatMap({ <-$0 }) {
        print("\(prime) is prime")
    }
    
    opened by hartbit 2
  • Cancel notification

    Cancel notification

    Summary

    Added the ability to a coroutine to call a custom handle when it is cancelled or terminated. It is important to give the ability to a programmer to respond to a coroutine cancellation/end of execution since it can add a lot of flexibility.

    Design decisions

    The handle is a simples closure that receives as parameter a boolean value. If true, the coroutine was cancelled. Otherwise, it is terminating gracefully.

    The handle will be called immediately before the coroutine handle is called. So, the coroutine have the ability to end gracefully.

    opened by ronflima 1
  • how to cooperate with ObjC?

    how to cooperate with ObjC?

    Use this template for simple enhancements that do not need a fully-fledged proposal.

    How to cooperate with ObjC code?

    Description

    Is there a way to cooperate with ObjC code? For example , my project is mixed with ObjC code and Swift code, How can this 2 languages use this library and the API supports each other?

    Is there any suggestion? Thanks a lot!

    opened by alexlee002 0
  • Refactoring and Updates

    Refactoring and Updates

    This implements several discussed changes to Venice:

    1. Bundles libdill directly, this avoids the need to use an apt/brew package
    2. Adds coroutine local storage support
    3. Refactors coroutines so they manage their own lifetime and clean up automatically
    opened by robertjpayne 2
Releases(0.20.0)
Owner
Zewo
Open source libraries for modern server software.
Zewo
AsyncOperators brings some features of RxSwift/Combine to Structured Concurrency

AsyncOperators brings some features of RxSwift/Combine to Structured Concurrency, such as combineLatest and distinctUntilChanged.

Ben Pious 3 Jan 18, 2022
Tools for using Swift Concurrency on macOS 10.15 Catalina, iOS 13, tvOS 13, and watchOS 6.

ConcurrencyCompatibility Tools for using Swift Concurrency on macOS 10.15 Catalina, iOS 13, tvOS 13, and watchOS 6. Xcode 13.2 adds backwards deployme

Zachary Waldowski 9 Jan 3, 2023
Slack message generator and API client, written in Swift with Result Builders and Concurrency

Slack Message Client This package provides a Swift object model for a Slack Block Kit message, as well as a Result Builder convenience interface for e

Mike Lewis 2 Jul 30, 2022
A complete set of primitives for concurrency and reactive programming on Swift

A complete set of primitives for concurrency and reactive programming on Swift 1.4.0 is the latest and greatest, but only for Swift 4.2 and 5.0 use 1.

AsyncNinja 156 Aug 31, 2022
A Swift DSL that allows concise and effective concurrency manipulation

NOTE Brisk is being mothballed due to general incompatibilities with modern version of Swift. I recommend checking out ReactiveSwift, which solves man

Jason Fieldman 25 May 24, 2019
⚡️ Fast async task based Swift framework with focus on type safety, concurrency and multi threading

Our apps constantly do work. The faster you react to user input and produce an output, the more likely is that the user will continue to use your appl

Said Sikira 814 Oct 30, 2022
The projects and materials that accompany the Modern Concurrency in Swift book

Modern Concurrency in Swift: Materials This repo contains all the downloadable materials and projects associated with the Modern Concurrency in Swift

raywenderlich 137 Dec 16, 2022
Several synchronization primitives and task synchronization mechanisms introduced to aid in modern swift concurrency.

AsyncObjects Several synchronization primitives and task synchronization mechanisms introduced to aid in modern swift concurrency. Overview While Swif

SwiftyLab 20 Jan 3, 2023
A Modern Concurrency and Synchronization for Swift.

##Features Simple Atomic<T> class for numbers and strings. Uncomplicated dispatch keyword for firing off background routines. Awesome Chan<T> for conc

Josh Baker 421 Jun 30, 2022
Swift concurrency collection support

AsyncCollections Functions for running async processes on Swift Collections ForEach Run an async function on every element of a Sequence. await array.

Adam Fowler 11 Jul 11, 2022
An introduction to using Swift's new concurrency features in SwiftUI

SwiftUI Concurrency Essentials An introduction to using Swift's new concurrency features in SwiftUI Discuss with me · Report Bug · Request Feature Art

Peter Friese 80 Dec 14, 2022
Type-safe networking with Swift Concurrency

AsyncRequest AsyncRequest is a type-safe framework for building a suite of requests to communicate with an API, built on top of Swift Concurrency. Ins

Light Year Software, LLC 1 Feb 9, 2022
A declarative state management and dependency injection library for SwiftUI x Concurrency

A declarative state management and dependency injection library for SwiftUI x Concurrency

Ryo Aoyama 199 Jan 1, 2023
Functional Concurrency Primitives

Concurrent Concurrent is a collection of functional concurrency primitives inspired by Concurrent ML and Concurrent Haskell. Traditional approaches to

TypeLift 206 Dec 24, 2022
Pomodoro is a macOS status bar application written in SwiftUI, which allows you to control your work and break time, exploiting the pomodoro-technique.

Pomodoro Pomodoro is a macOS status bar application which allows you to control your work and break time. Through this application you will be able to

Cristian Turetta 7 Dec 28, 2022
THORChain vault churn countdown timer for macOS tray

ChurnCountdown Useful macOS tray app to show THORChain churn countdown. On start

null 0 Jan 5, 2022
Async and concurrent versions of Swift’s forEach, map, flatMap, and compactMap APIs.

CollectionConcurrencyKit Welcome to CollectionConcurrencyKit, a lightweight Swift package that adds asynchronous and concurrent versions of the standa

John Sundell 684 Jan 9, 2023
Automatically generate GraphQL queries and decode results into Swift objects, and also interact with arbitrary GitHub API endpoints

GitHub API and GraphQL Client This package provides a generic GitHub API client (GithubApiClient) as well as Codable-like GitHub GraphQL querying and

Mike Lewis 4 Aug 6, 2022
GroupWork is an easy to use Swift framework that helps you orchestrate your concurrent, asynchronous functions in a clean and organized way

GroupWork is an easy to use Swift framework that helps you orchestrate your concurrent, asynchronous functions in a clean and organized way. This help

Quan Vo 42 Oct 5, 2022