Csp scala wixmeetup2016

40
Wix R&D meetup; CSP & Scala: theory and implementation. https://github.com/rssh/scala-gopher Ruslan Shevchenko <[email protected]> https://github.com/rssh @rssh1

Transcript of Csp scala wixmeetup2016

Wix R&D meetup;

CSP & Scala: theory and implementation. https://github.com/rssh/scala-gopher

Ruslan Shevchenko<[email protected]>

https://github.com/rssh

@rssh1

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. Theory & History

❖ If you familiar with basic concepts, skip to slide 8

❖ Just show me scala API: skip to slide 14

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. 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. CSP Notation(basic)

// after event

Operations on processes:

// fix point (loops, recursion)

// interleave concurrently

// choice, if a then P if b then Q

// seq

7. CSP Notation(examples)

COPY(left,right)=

left right

P1 P2

Q1 Q2

P1 P2

Q1 Q2

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. 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. 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 ForwardDavid Leo PresottoRob PikeDennis M. RitchieKen Thompson

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. Go: simple code examplefunc fibonacci(c chan int, quit chan bool) { go {

x, y := 0, 1for (){

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. Go: simple code examplefunc fibonacci(c chan int, quit chan bool) { go {

x, y := 0, 1for (){

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. 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

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. 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._

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 ?)

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

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

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

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. 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() }

23. scala-gopher: select de-‘macro’

s.reading(channel1, x => (do-something-1))

s.onRead(channel1, x => { implicit f1: FlowTermination = s.flowTermination implicit e1: ExecutionContext => s.executionContext scala.async.Async.async( transfer(do-something-1) )}

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. 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. 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. scala-gopher: Timeoutsval (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. 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. Example: broadcast without listener registrycase 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. 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. 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)

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. CPS / Scala

❖ Test of

❖ essential CPS functionality

❖ dynamic channels

❖ state inside selector folds

❖ More : Transputers

34. Transputers❖ Structure => schemes.

❖ Entity,

❖ set of input ports

❖ set of output ports

❖ logic

❖ Process/Transputer

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. 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 inXbingo.inY connect inYbingo.out connect acceptor(bingo + acceptor).start()

37. Transputers

❖ Selector (Custom logic)

❖ Par (|| execution)

❖ Replicate

38. Scala concurrency ecosystem

❖ Future [?]

❖ Erlang-Style ? (actors)

❖ Ozz-Style ? (async [partially])

❖ CPS (gopher)

❖ Rx [?]

❖ Can be used together.

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. Questions?

❖ It was about: https://github.com/rssh/scala-gopher

❖ Questions ?

❖ Thanks for attention.