Join the chat at https://gitter.im/chymyst-core/Lobby Build Status Coverage Status License Github Tag Maven Central

Chymyst — declarative concurrency in Scala

This repository hosts Chymyst Core — a library that provides a Scala domain-specific language for declarative concurrency. Chymyst is a framework-in-planning that will build upon Chymyst Core to enable creating concurrent applications declaratively.

Chymyst is based on the chemical machine paradigm, known in the academic world as Join Calculus (JC). JC has the same expressive power as CSP (Communicating Sequential Processes) and the Actor model, but is easier to use. (See also Conceptual overview of concurrency.)

The initial code of Chymyst Core was based on previous work by Jiansen He (2011) and Philipp Haller (2008), as well as on Join Calculus prototypes in Objective-C/iOS and Java/Android (2012).

The current implementation is tested under Oracle JDK 8 with Scala 2.11.8 and 2.12.1.

Version history and roadmap

Overview of Chymyst and the chemical machine paradigm

Get started with this extensive tutorial book

A complete minimal “Hello, world” project

Video presentation of early version of Chymyst Core, then called JoinRun

This talk was given at Scalæ by the Bay 2016. See also these talk slides revised for the current syntax.

Main features of the chemical machine

Comparison of the chemical machine vs. academic Join Calculus

Comparison of the chemical machine vs. the Actor model

Comparison of the chemical machine vs. the coroutines / channels approach (CSP)

Technical documentation for Chymyst Core.

Example: “dining philosophers”

This is a complete runnable example. The logic of “dining philosophers” is implemented in a completely declarative and straightforward code.

import io.chymyst.jc._

object Main extends App {
   /** Print message and wait for a random time interval. */
  def wait(message: String): Unit = {
    println(message)
    Thread.sleep(scala.util.Random.nextInt(20))
  }
  
  val hungry1 = m[Int]
  val hungry2 = m[Int]
  val hungry3 = m[Int]
  val hungry4 = m[Int]
  val hungry5 = m[Int]
  val thinking1 = m[Int]
  val thinking2 = m[Int]
  val thinking3 = m[Int]
  val thinking4 = m[Int]
  val thinking5 = m[Int]
  val fork12 = m[Unit]
  val fork23 = m[Unit]
  val fork34 = m[Unit]
  val fork45 = m[Unit]
  val fork51 = m[Unit]
  
  site (
    go { case thinking1(_) => wait("Socrates is thinking");  hungry1() },
    go { case thinking2(_) => wait("Confucius is thinking"); hungry2() },
    go { case thinking3(_) => wait("Plato is thinking");     hungry3() },
    go { case thinking4(_) => wait("Descartes is thinking"); hungry4() },
    go { case thinking5(_) => wait("Voltaire is thinking");  hungry5() },
  
    go { case hungry1(_) + fork12(_) + fork51(_) => wait("Socrates is eating");  thinking1() + fork12() + fork51() },
    go { case hungry2(_) + fork23(_) + fork12(_) => wait("Confucius is eating"); thinking2() + fork23() + fork12() },
    go { case hungry3(_) + fork34(_) + fork23(_) => wait("Plato is eating");     thinking3() + fork34() + fork23() },
    go { case hungry4(_) + fork45(_) + fork34(_) => wait("Descartes is eating"); thinking4() + fork45() + fork34() },
    go { case hungry5(_) + fork51(_) + fork45(_) => wait("Voltaire is eating");  thinking5() + fork51() + fork45() }
  )
  // Emit molecules representing the initial state:
  thinking1() + thinking2() + thinking3() + thinking4() + thinking5()
  fork12() + fork23() + fork34() + fork45() + fork51()
  // Now reactions will start and print messages to the console.
}

Status

The Chymyst Core library is in alpha pre-release, with very few API changes envisioned for the future.

The semantics of the chemical machine (restricted to single-host, multicore computations) is fully implemented and tested on many nontrivial examples.

The library JAR is published to Maven Central.

Extensive tutorial and usage documentation is available.

Unit tests include examples such as concurrent counters, parallel “or”, concurrent merge-sort, and “dining philosophers”. Test coverage is 100% according to codecov.io.

Performance benchmarks indicate that Chymyst Core can schedule about 10,000 reactions per second per CPU core, and the performance bottleneck is in submitting jobs to threads (a distant second bottleneck is pattern-matching in the internals of the library).

Known limitations:

  • Chymyst Core is about 2x slower than Jiansen He’s ScalaJoin on the blocking molecule benchmark, and about 1.2x slower on some non-blocking molecule benchmarks.
  • Chymyst Core has no fairness with respect to the choice of molecules: If a reaction could proceed with many alternative sets of input molecules, the input molecules are not chosen at random.
  • Chymyst Core has no distributed execution (Jiansen He’s Disjoin.scala is not ported to Chymyst, and probably will not be). Distributed computation should be implemented in a better way than posting channel names on an HTTP server. (However, Chymyst Core will use all cores on a single machine.)

Run unit tests

sbt test

The tests will print some error messages and exception stack traces - this is normal, as long as all tests pass.

Some tests are timed and will fail on a slow machine.

Run the benchmark application

sbt benchmark/run will run the benchmarks.

To build the benchmark application as a self-contained JAR, run

sbt clean benchmark/assembly

Then run it as

java -jar benchmark/target/scala-2.11/benchmark-assembly-*.jar

To run with FlightRecorder:

sbt benchmark/runFR

This will create the file benchmark.jfr in the current directory. Open that file with jmc (Oracle’s “Java Mission Control” tool) to inspect Code and then the “Hot Methods” (places where most time is spent).

Use Chymyst Core in your programs

Chymyst Core is published to Maven Central. To pull the dependency, add this to your build.sbt at the appropriate place:

libraryDependencies += "io.chymyst" %% "chymyst-core" % "latest.integration"

To use the chemical machine DSL, add import io.chymyst.jc._ in your Scala sources.

See the “hello, world” project for a complete minimal example.

Build the library JARs

To build the library JARs:

sbt core/package core/package-doc

This will prepare JAR assemblies as well as their Scaladoc documentation packages.

The main library is in the core JAR assembly (core/target/scala-2.11/core-*.jar). User code should depend on that JAR only.

Publish to Sonatype

$ sbt
> project core
> +publishSigned
> sonatypeRelease

Trivia

Robert Boyle’s self-flowing flask

This drawing is by Robert Boyle, who was one of the founders of the science of chemistry. In 1661 he published a treatise titled “The Sceptical Chymyst”, from which the Chymyst framework borrows its name.

Related Repositories

chymyst-core

chymyst-core

Declarative concurrency in Scala - The implementation of the chemical machine ...