Giter VIP home page Giter VIP logo

reagents.jl's Introduction

Reagents.jl: Towards composable and extensible nonblocking programming for Julia

Reagents.jl implements reagents (Turon, 2012) which provides higher-order concurrency primitives for expressing nonblocking algorithms and concurrent synchronizations in a composable manner.

For example, op1 | op2 is the combinator that combines the "lazy" representation of operations (called reagents) and expresses that only one of the operations take place. This is similar to Go's select statement on channels (Ref: specification) but Reagents.jl has other combinators and is extensible to any user-defined data structures.

Note: Due to the simplistic implementation of the k-CAS, Reagents.jl is not yet nonblocking in the strict sense. However, as discussed in Turon (2012), it should be straightforward to switch to a k-CAS algorithm with more strict guarantee.

Example: Treiber stack

Let us implement Treiber stack which can be represented as an atomic reference to an immutable list:

using Reagents

struct Node{T}
    head::T
    tail::Union{Node{T},Nothing}
end

const List{T} = Union{Node{T},Nothing}

struct TreiberStack{T,Ref<:Reagents.Ref{List{T}}}
    head::Ref
end

TreiberStack{T}() where {T} = TreiberStack(Reagents.Ref{List{T}}(nothing))

The push and pop operations can be expressed as reagents:

pushing(stack::TreiberStack) =
    Reagents.Update((xs, x) -> (Node(x, xs), nothing), stack.head)

popping(stack::TreiberStack) =
    Reagents.Update(stack.head) do xs, _
        if xs === nothing
            return (nothing, nothing)
        else
            return (xs.tail, xs.head)
        end
    end

The execution ("reaction") of the reagent can be invoked by just calling the reagent object. So, it's straightforward to wrap it in the standard function API:

Base.push!(stack::TreiberStack, value) = pushing(stack)(value)
Base.pop!(stack::TreiberStack) = popping(stack)()

These user-defined reagents can be composed just like pre-defined reagents. For example, we can move an element from one stack to another by using the sequencing combinator :

s1 = TreiberStack{Int}()
s2 = TreiberStack{Int}()
push!(s1, 1)
(popping(s1) ⨟ pushing(s2))()
@assert pop!(s2) == 1

Here, the element in the stack s1 is popped and then pushed to the stack s2 atomically. Similar code works with arbitrary pair of containers, possibly of different types.

For more examples, see the examples directory.

Resources

reagents.jl's People

Contributors

tkf avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.