This document outlines a presentation about CSP (Communicating Sequential Processes) theory and implementation in Scala using the scala-gopher library. It begins with an introduction to CSP theory, history, and languages like Occam and Limbo that were influenced by it. It then demonstrates how scala-gopher implements key CSP concepts like channels, selectors, and processes in Scala. Examples are shown of writing Fibonacci sequences and broadcasting messages using these primitives. Potential issues with the current scala-gopher implementation are noted.
1. Wix R&D meetup;
CSP & Scala: theory and
implementation.
https://github.com/rssh/scala-gopher
Ruslan Shevchenko
<ruslan@shevchenko.kiev.ua>
https://github.com/rssh
@rssh1
2. 2. Outline
❖ Theory & History
❖ CSP = Communication Sequence Processes
❖ !-calculus (CCS = Calculus of Communicating System)
❖ Languages: (Occam,Limbo, Go[Clojure, Scala, … ])
❖ Main constructions / idioms, how they looks
❖ Channels, Selectors, Transputers
❖ Implementation Techniques
3. 3. Theory & History
❖ If you familiar with basic concepts, skip to slide 8
❖ Just show me scala API: skip to slide 14
4. 4. Theory & History
❖ CPS = Communication Sequence Processes.
❖ CSS = Calculus of Communicating System.
❖ 1978 First CSP Paper by Tony Hoar.
❖ 1980. CSS by Robert Milner. (Algebraic Processes)
❖ 1985 CSP formalism (influenced by CSS) in CSP Book
❖ http://www.usingcsp.com/
❖ 1992 !-calculus [CSS + Channels] (Robert Milner, Joachim Parrow,
David Walker)
❖ …. (large family of Process Algebras, including Actor Model, ACP, )
5. 5. CSP Notation(basic)
❖ (A, B, C … ) — processes,
❖ (x, y, z … ) — events
❖ atomic: x , STOP, BEEP, or c.v (channel/value)
❖ c!v - send v to channel c (after this c.v happens)
❖ c?v - receive v from channel c (wait until c.v)
❖ trace(…) — sequence of events.
6. 6. CSP Notation(basic)
// after event
Operations on processes:
// fix point (loops, recursion
// interleave concurrently
// choice, if a then P if b then Q
// seq
8. 8. Occam language
William Occam, 1287-1347
‘Occam razor’ principle
Occam language. (1983)
INT x, y:
SEQ
x := 4
y := (x + 1)
CHAN INT c:
PAR
some.procedure (x, y, c!)
another.procedure (c?)
y := 5
braces via indentation. (before python)
minimal language. (processor constructors)
9. 9. Occam language
❖ created by INMOS
❖ targeted Transputers CHIP architecture (bare metal)
❖ latest dialect: occam-! [1996]
❖ extensions from !-calculus
❖ (more dynamic, channels can be send via channels)
❖ http://concurrency.cc — occam for Aurdino.
10. 10. Inferno, Limbo, Go
❖ Unix, Plan9, Inferno: [At&t; Bell labs; vita nuova]
❖ http://www.vitanuova.com/inferno/
❖ Limbo … C-like language + channels + buffered channels.
❖ Go … channels as in Limbo (Go roots is from hell is not a metaphor)
Sean Forward
David Leo Presotto
Rob Pike
Dennis M. Ritchie
Ken Thompson
11. 11. CPS in Limbo/Go/Scala: main constructions
❖ processes: operator for spawning new lightweight
process.
❖ channels: can be unbuffered (synchronised) or buffered
❖ unbuffered — writer wait until reader start to work
❖ buffered — if channel buffer is not full, writer not
blocked
❖ selector: wait for few events (channel), eval first.
12. 12. Go: simple code example
func fibonacci(c chan int, quit chan bool) {
go {
x, y := 0, 1
for (){
select {
case c <- x :
x, y = y, x+y
case q<-quit:
break;
}
}
close(c)
}
}
c = make(chan int);
quit= make(chan bool);
fibonacci(c,quit)
for i := range c {
fmt.Println(i)
if (i > 2000) {
quit <- true
}
}
Go
13. 13. Go: simple code example
func fibonacci(c chan int, quit chan bool) {
go {
x, y := 0, 1
for (){
select {
case c <- x :
x, y = y, x+y
case q<- quit:
break;
}
}
close(c)
}
}
c = make(chan int);
quit= make(chan bool);
fibonacci(c,quit)
for i := range c {
fmt.Println(i)
if (i > 2000) {
quit <- true
}
}
Go
14. 14. scala-gopher
❖ Akka extension + Macros on top of SIP22-async
❖ Integrate CSP primitives and scala concurrency
❖ Provide:
❖ asynchronous API inside general control-flow
❖ pseudo-synchronous API inside go{ .. } or async{ ..}
blocks
15. def fibonacci(c: Channel[Int], quit: Channel[Boolean]): Future[Unit] = go {
var (x, y) = (0, 1)
select.forever{
case x : c.write =>
val z = x
x = y; y = x+z
case q: quit.read =>
CurrentFlowTermination.exit(())
}
c.close()
}
15. Scala: simple code example (direct translation)
val c = makeChannel[Int]()
val quit= makeChannel[Boolean]();
val f = fibonacci(c,quit)
for ( i <- c) {
System.out.print(i)
if (i > N) {
quit.awrite( true )
Scala
16. 16. Scala-gopher: main constructions
❖ Gopher: Akka extension.
❖ go[T](body: T): Future[T]
❖ inside body we can use pseudosynchronous API
❖ defer
❖ select [forever, once] => [choice]
❖ case matching macros; for statement, fold, ..
❖ channels
❖ channels/ Input[X]/Output[X] / collection API
val gopherApi = GopherAPI(actorsSystem)
import gopherApi._
17. def fibonacci(c: Channel[Int], quit: Channel[Boolean]): Future[Unit] = go {
var (x, y) = (0, 1)
select.forever{
case x : c.write =>
val z = x
x = y; y = x+z
case q: quit.read =>
CurrentFlowTermination.exit(())
}
c.close()
}
}
17. Scala: simple code example (direct translation)
val c = makeChannel[Int]()
val quit= makeChannel[Boolean]();
val f = fibonacci(c,quit)
for ( i <- c) {
System.out.print(i)
if (i > N) {
quit.awrite( true )
Scala
(mutable var, WTF ?)
18. def fibonacci(c: Channel[Int], quit: Channel[Boolean]): Future[Unit] = go {
select.fold((0,1)){ case ((x,y),s) =>
s match {
case x : c.write =>
(y, x+y)
case q: quit.read =>
CurrentFlowTermination.exit((x,y))
}
}
c.close()
}
18. Scala: simple code example (fold)
val c = makeChannel[Int]()
val quit= makeChannel[Boolean]();
val f = fibonacci(c,quit)
for ( i <- c) {
System.out.print(i)
if (i > N) {
quit.awrite( true )
Scala
19. def fibonacci(c: Channel[Int], quit: Channel[Boolean]): Future[Unit] = go {
val (x,y) = select.fold((0,1)){ case ((x,y),s) =>
s match {
case x : c.write =>
(y, x+y)
case q: quit.read =>
CurrentFlowTermination.exit((x,y))
}
}
c.close()
}
19. Scala: simple code example (fold)
val c = makeChannel[Int]()
val quit= makeChannel[Boolean]();
val f = fibonacci(c,quit)
for ( i <- c) {
System.out.print(i)
if (i > N) {
quit.awrite( true )
Scala
20. def fibonacci(c: Channel[Int], quit: Channel[Boolean]): Future[(Int,Int)] =
select.afold((0,1)){ case ((x,y),s) =>
s match {
case x : c.write =>
(y, x+y)
case q: quit.read =>
c.close()
CurrentFlowTermination.exit((x,y))
}
}
20. Scala: simple code example (аfold)
val c = makeChannel[Int]()
val quit= makeChannel[Boolean]();
val f = fibonacci(c,quit)
for ( i <- c) {
System.out.print(i)
if (i > N) {
quit.awrite( true )
Scala
21. s match {
case x : c.write =>
(y, x+y)
case q: quit.read =>
c.close()
CurrentFlowTermination.exit((x,y))
}
21. scala-gopher: select
Scala
s match {
case z : c.write if (z == 1) =>
……..
case x: Int if (x == future.read()) =>
}
//special syntax
//writing expressions
//reading from expressions
22. 22. scala-gopher: select de-‘macro’
select.forever{
case x: channel1.read => (do-something-1)
case y: channel2.read => (do-something-2)
case z: channel3.write => (do-something-3)
case _ => (do-somethig-4)
}
{ val s = select.createForeverSelectorBuilder()
s.reading(channel1, x => (do-something-1))
s.reading(channel2, y => (do-something-2))
s.write(channel3,z,….)
s.idle(do-something-4)
s.run()
}
24. 24. scala-gopher: select functionality
❖ select functionality
❖ wrap callbacks, supplied by user
❖ to guarantee than only one branch of select match
is running
❖ to provide reaction on flowTermination
❖ pass one to channels/inputs/outpus
25. 25. scala-gopher: Input/Output.
❖ Input[T] - something, from which we can wait input
❖ callback-based ‘native’ trait.
❖ implementations: channels, futures, collections …
❖ collection-like methods: map, filter, fold, zip,
append, merge…
aread(): Future[T]
read: T = await(aread)
<~ = read.
26. 26. scala-gopher: Input/Output.
❖ Output[T] - something, where we can send value
❖ callback-based ‘native’ trait.
❖ implementations: channels, promises, actor-s…
awrite(v:T): Future[T]
write(t): T = await(awrite(t))
~> = write.
27. 27. scala-gopher: Timeouts
val (in, inTimeouts) = in.withTimeout(1 minute)
val (out, outTimeouts) = out.withTimeout(10 minutes)
select.fold(s0){ (state,selector) =>
selector match {
case x: in.read => out.write(x)
case t: inTimeouts.read =>
log(“input timeout”);
case t: outTimeouts.read =>
log(“output timeout”);
}
}
28. 28. Example: broadcast without listener registry
❖ Sender:
❖ create buffered channel for each message
❖ send into this channel: message + channel for next message
❖ can report value of channel for next message
❖ Receiver:
❖ read message from channel,
❖ put one back (to allow other readers to receive one)
❖ change channel to listen on just receive.
29. 29. Example: broadcast without listener registry
case class Message[V](
value: Value,
nextChannel: Channel[Message[V]]
)
class Receiver[V](initChannel: Message, handle: V => Unit)
{
val current = makeEffectedChannel(initChannel)
val future = current.forever{ message =>
current write message.value
current := message.nextChannel
go { handle(message.value) }
}
}
30. 30. Example: broadcast without listener registry
class Broadcaster[V]() // single-threaded
{
var last = makeNextChannel
def send(v: V) : Unit = {
val next = makeNextChannel
last write Message(v, next)
last = next
}
def makeReceiver(handler: V => Unit) =
new Receiver(current, next)
def makeNextChannel = makeChannel[Message[V]](1)
}
31. 31. Example: broadcast without listener registry
class Broadcaster[V]()
{
val sendc = makeChannel[V]()
val requestLast = makeChannel[Channel[Channel[Value[V]]]()
val future = select.fold(makeNextChannel){ (last,s) =>
s match {
case v:sendc.read => val next = makeNextChannel
last <~ Message(v,next)
next
case r:requestLast.read =>
r <~ last
last
}
}
def send(v: V) : Unit = sendc.send(v)
32. 31. Example: broadcast without listener registry
class Broadcaster[V]()
{
val sendc = makeChannel[V]()
val requestLast = makeChannel[Channel[Channel[Value[V]]]()
val future = select.fold(makeNextChannel){ (last,s) =>
s match {
case v:sendc.read => val next = makeNextChannel
last <~ Message(v,next)
next
case r:requestLast.read =>
r <~ last
last
}
}
def makeReceiver(handler): Future[Receiver] = go {
val r = makeChannel[Channel[Message[V]]
requestLast <- r
Receiver(await(r.read),handler)
33. 33. CPS / Scala
❖ Test of
❖ essential CPS functionality
❖ dynamic channels
❖ state inside selector folds
❖ More : Transputers
34. 34. Transputers
❖ Structure => schemes.
❖ Entity,
❖ set of input ports
❖ set of output ports
❖ logic
❖ Process/Transputer
35. 35 Transputers.
trait Bingo extends Transputer
{
val inX = inPort[Int]()
val inY = inPort[Int]()
val out = OutPort[String]()
loop {
case x: inX.read =>
val y = inY.read
if (x == y) {
out <~ “Bingo!”
}
}
}
36. 36. Transputers
❖ like actors, but for channels
❖ can be supervised ‘as actors’.
❖ can be connected to each other
val (inX,inY) = (makeChannel[Int](), makeChannel[Int])
val bing = makeTransputer[Bingo]
val acceptor = makeTransputer[Acceptor]
bingo.inX connect inX
bingo.inY connect inY
bingo.out connect acceptor
(bingo + acceptor).start()
38. 38. Scala concurrency ecosystem
❖ Future [?]
❖ Erlang-Style ? (actors)
❖ Ozz-Style ? (async [partially])
❖ CPS (gopher)
❖ Rx [?]
❖ Can be used together.
39. 39. Scala-Gopher, problems
❖ async implementation
❖ (buggy, not complete)
❖ need application programmer use macros for providing
‘pseudo synchronised’ API
❖ Need more experience.
❖ // details will be in ScalaUA talk.
40. 40. Questions?
❖ It was about: https://github.com/rssh/scala-gopher
❖ Questions ?
❖ Thanks for attention.