C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and...

82
C. Varela; Adapted with permission from S. Haridi and P. Van Roy 1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted with permission from: Seif Haridi KTH Peter Van Roy UCL
  • date post

    20-Dec-2015
  • Category

    Documents

  • view

    219
  • download

    2

Transcript of C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and...

Page 1: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 1

Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8)

Carlos Varela

RPI

Adapted with permission from:

Seif Haridi

KTH

Peter Van Roy

UCL

Page 2: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 2

Overview of concurrent programming

• Four basic approaches to programming are:– Sequential programming (no concurrency)

– Declarative concurrency (streams in a functional language, Oz)

– Message passing with active objects (Erlang, SALSA)

– Atomic actions on shared state (Java)

• The atomic action approach is the most difficult, yet it is the one you will probably be most exposed to!

• But, if you have the choice, which approach to use?– Use the simplest approach that does the job: sequential if that is

ok, else declarative concurrency if there is no observable nondeterminism, else message passing if you can get away with it.

Page 3: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 3

Concurrency

• Some programs are best written as a set of activities that run independently (concurrent programs)

• Concurrency is essential for interaction with the external environment– Examples includes GUI (Graphical User Interfaces), operating systems,

web services

– Also programs that are written independently but interact only when needed (client-server, peer-to-peer applications)

• First, we will cover declarative concurrency, programs with no observable nondeterminism, the result is a function– Independent procedures that execute at their own pace and may

communicate through shared dataflow variables

• Then, we will cover message passing, programs consisting of components with encapsulated state communicating asynchronously

Page 4: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 4

Overview of declarative concurrency

• Programming with threads– The model is augmented with threads

– Programming techniques: stream communication, order-determining concurrency, concurrent composition

• Lazy execution– demand-driven computations, lazy streams

• Soft real-time programming

Page 5: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 5

The sequential model

w = az = person(age: y)xy = 42u

Single-assignmentstore

SemanticStack

Statements areexecuted sequentiallyfrom a single semanticstack

Page 6: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 6

The concurrent model

w = az = person(age: y)xy = 42u

Single-assignmentstore

SemanticStack 1

SemanticStack N

Multiple semanticstacks (threads)

Page 7: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 7

Concurrent declarative model

s ::= skip empty statement | x = y variable-variable binding

| x = v variable-value binding

| s1 s2 sequential composition| local x in s1 end declaration| proc {x y1 … yn } s1 end procedure introduction| if x then s1 else s2 end conditional| { x y1 … yn } procedure application| case x of pattern then s1 else s2 end pattern matching| thread s1 end thread creation

The following defines the syntax of a statement, s denotes a statement

Page 8: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 8

The concurrent model

Single-assignmentstore

STthread s1 end,ETop of Stack, Thread i

Page 9: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 9

The concurrent model

Single-assignmentstore

STTop of Stack, Thread i s1,E

Page 10: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 10

Basic concepts

• The model allows multiple statements to execute ”at the same time”.

• Imagine that these threads really execute in parallel, each has its own processor, but share the same memory

• Reading and writing different variables can be done simultaneously by different threads, as well as reading the same variable

• Writing the same variable is done sequentially• The above view is in fact equivalent to an interleaving

execution: a totally ordered sequence of computation steps, where threads take turn doing one or more steps in sequence

Page 11: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 11

Causal order

• In a sequential program all execution states are totally ordered

• In a concurrent program all execution states of a given thread are totally ordered

• The execution state of the concurrent program as a whole is partially ordered

Page 12: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 12

Total order

• In a sequential program all execution states are totally ordered

computation step

sequentialexecution

Page 13: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 13

Causal order in the declarative model

• In a concurrent program all execution states of a given thread are totally ordered

• The execution state of the concurrent program is partially ordered

computation step

thread T1

thread T2

thread T3

fork a thread

Page 14: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 14

Causal order in the declarative model

computation step

thread T1

thread T2

thread T3

fork a thread

bind a dataflow variable

synchonize on a dataflow variable

x

y

Page 15: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 15

Nondeterminism

• An execution is nondeterministic if there is a computation step in which there is a choice what to do next

• Nondeterminism appears naturally when there is concurrent access to shared state

Page 16: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 16

Example of nondeterminism

time

Thread 1

x = 1

xy = 5

store

time

Thread 2

x = 3

The thread that binds x first will continue,the other thread will raise an exception

Page 17: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 17

Nondeterminism

• An execution is nondeterministic if there is a computation step in which there is a choice what to do next

• Nondeterminism appears naturally when there is concurrent access to shared state

• In the concurrent declarative model when there is only one binder for each dataflow variable, the nondeterminism is not observable on the store (i.e. the store develops to the same final results)

• This means for correctness we can ignore the concurrency

Page 18: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 18

Scheduling

• The choice of which thread to execute next and for how long is done by a part of the system called the scheduler

• A thread is runnable if its next statement to execute is not blocked on a dataflow variable, otherwise the thread is suspended

• A scheduler is fair if it does not starve a runnable thread, i.e. all runnable threads eventually execute

• Fair scheduling makes it easy to reason about programs and program composition

• Otherwise some correct program (in isolation) may never get processing time when composed with other programs

Page 19: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 19

The semantics

• In the sequential model we had:

(ST , )

ST is a stack of semantic statements

is the single assignment store

• In the concurrent model we have:

(MST , )

MST is a (multi)set of stacks of semantic statements

is the single assignment store

Page 20: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 20

The initial execution state

({ [ (s,) ] }, )

statement

stack

multisetstore

Page 21: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 21

Execution (the scheduler)

• At each step, one runnable semantic stack is selected from MST (the multiset of stacks), call it ST, s.t. MST = ST MST’

• Assume the current store is , one computation step is done that transforms ST to ST’ and to ’

• The total computation state is transformed from (MST, ) to (ST’ MST’, ’)

• Which stack is selected, and how many steps are taken is the task of the scheduler, a good scheduler should be fair, i.e., each runnable ’thread’ will eventually be selected

• The computation stops when there are no runnable stacks

Page 22: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 22

Example of runnable threadsproc {Loop P N} if N > 0 then {P} {Loop P N-1} else skip endendthread {Loop

proc {$} {Show 1} end 1000}

endthread {Loop

proc {$} {Show 2} end

1000}end

• This program will interleave the execution of two threads, one printing 1, and the other printing 2

• We assume a fair scheduler

Page 23: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 23

Dataflow computation

• Threads suspend on data unavailability in dataflow variables

• The {Delay X} primitive makes the thread suspends for X milliseconds, after that, the thread is runnable

declare X{Browse X}local Y in thread {Delay 1000} Y = 10*10 end X = Y + 100*100end

Page 24: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 24

Illustrating Dataflow computation

• Enter incrementally the values of X0 to X3

• When X0 is bound the thread will compute Y0=X0+1, and will suspend again until X1 is bound

declare X0 X1 X2 X3{Browse [X0 X1 X2 X3]}thread Y0 Y1 Y2 Y3in {Browse [Y0 Y1 Y2 Y3]} Y0 = X0 + 1 Y1 = X1 + Y0 Y2 = X2 + Y1 Y3 = X3 + Y2 {Browse completed}end

Page 25: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 25

Concurrent Map

fun {Map Xs F} case Xs of nil then nil [] X|Xr then

thread {F X} end|{Map Xr F} endend

• This will fork a thread for each individual element in the input list

• Each thread will run only if both the element X and the procedure F is known

Page 26: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 26

Concurrent Map Functionfun {Map Xs F}

case Xs of nil then nil [] X|Xr then thread {F X} end |{Map Xr F} end

end

• What this looks like in the kernel language:proc {Map Xs F Rs}

case Xs of nil then Rs = nil [] X|Xr then R Rr in Rs = R|Rr thread R = {F X} end {Map Xr F Rr} end

end

Page 27: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 27

How does it work?

• If we enter the following statements:declare F X Y Z{Browse thread {Map X F} end}

• A thread executing Map is created.

• It will suspend immediately in the case-statement because X is unbound.

• If we thereafter enter the following statements:X = 1|2|Yfun {F X} X*X end

• The main thread will traverse the list creating two threads for the first two arguments of the list,

Page 28: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 28

How does it work?

• The main thread will traverse the list creating two threads for the first two arguments of the list

• thread {F 1} end, and thread {F 2} end, Y = 3|ZZ = nil

• will complete the computation of the main thread and the newly created thread thread {F 3} end, resulting in the final list [1 4 9].

Page 29: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 29

Cheap concurrency and dataflow

• Declarative programs can be easily made concurrent

• Just use the thread statement where concurrency is needed

fun {Fib X} if X=<2 then 1 else thread {Fib X-1} end + {Fib X-2} end end

Page 30: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 30

Understanding why

fun {Fib X} if X=<2 then 1 else F1 F2 in F1 = thread {Fib X-1} end

F2 = {Fib X-2}

F1 + F2end

end

Dataflow dependency

Page 31: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 31

Execution of {Fib 6}

F6

F5

F4 F2

F3

F2

F1

F2

F3

F2

F1

F4

F1F3

F2

Fork a thread

Synchronize onresult

Running thread

Page 32: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 32

Fib

Page 33: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 33

Streams

• A stream is a sequence of messages

• A stream is First-In First-Out (FIFO) channel

• The producer augments the stream with new messages, and the consumer reads the messages, one by one.

x5 x4 x3 x2 x1

producer consumer

Page 34: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 34

Stream Communication I

• The data-flow property of Oz easily enables writing threads that communicate through streams in a producer-consumer pattern.

• A stream is a list that is created incrementally by one thread (the producer) and subsequently consumed by one or more threads (the consumers).

• The consumers consume the same elements of the stream.

Page 35: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 35

Stream Communication II

• Producer, that produces incrementally the elements

• Transducer(s), that transforms the elements of the stream

• Consumer, that accumulates the results

producer transducer transducer consumer

thread 1 thread 2 thread 3 thread N

Page 36: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 36

Program patterns

• The producer, transducers, and the consumer can, in general, be described by certain program patterns

• We show the various patterns

Page 37: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 37

Producer

fun {Producer State} if {More State} then X = {Produce State} in X | {Producer {Transform State}}

else nil endend• The definition of More, Produce, and Transform is

problem dependent• State could be multiple arguments

• The above definition is not a complete program!

Page 38: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 38

Example Producer

fun {Generate N Limit} if N=<Limit then N | {Generate N+1 Limit} else nil end end

• The State is the two arguments N and Limit

• The predicate More is the condition N=<Limit

• The Transform function (N,Limit) (N+1,Limit)

fun {Producer State} if {More State} then X = {Produce State} in X | {Producer {Transform State}} else nil endend

Page 39: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 39

Consumer Pattern

fun {Consumer State InStream} case InStream of nil then {Final State} [] X | RestInStream then NextState = {Consume X State} in {Consumer NextState RestInStream} endend• Final and Consume are problem dependent

The consumer suspends untilInStream is either a cons or a nil

Page 40: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 40

Example Consumer

fun {Sum A Xs} case Xs of X|Xr then {Sum A+X Xr} [] nil then A end end • The State is A

• Final is just the identity function on State

• Consume takes X and State X + State

fun {Consumer State InStream} case InStream of nil then {Final State} [] X | RestInStream then NextState = {Consume X State} in {Consumer NextState RestInStream} endend

Page 41: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 41

Transducer Pattern 1

fun {Transducer State Instream} case InStream of nil then nil [] X | RestInStream then NextState#TX = {Transform X State} TX | {Transducer NextState RestInStream} endend• A transducer keeps its state in State, receives messages on

InStream and sends messages on OutStream

Page 42: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 42

Transducer Pattern 2

fun {Transducer State Instream} case InStream of nil then nil [] X | RestInStream then

if {Test X#State} then NextState#TX = {Transform X State} TX | {Consumer NextState RestInStream}

else {Consumer NextState RestInStream} end endend• A transducer keeps its state in State, receives messages on InStream

and sends messages on OutStream

Page 43: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 43

Example Transducer

fun {Filter Xs F} case Xs of nil then nil [] X|Xr then if {F X} then X|{Filter Xr F} else {Filter Xr F} end endend

Generate Filter

IsOdd

6 5 4 3 2 1 5 3 1

Filter is a transducer thattakes an Instream and incrementlyproduces an Outstream that satisfiesthe predicate F

local Xs Ys in thread Xs = {Generate 1 100} end thread Ys = {Filter Xs IsOdd} end thread {Browse Ys} endend

Page 44: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 44

Larger Example:The sieve of Eratosthenes

• Produces prime numbers• It takes a stream 2...N, peals off 2 from the rest of the stream• Delivers the rest to the next sieve

Sieve

Filter Sieve

Xs

Xr

X

Ys Zs

X|Zs

Page 45: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 45

Sieve

fun {Sieve Xs} case Xs of nil then nil [] X|Xr then Ys in thread Ys = {Filter Xr fun {$ Y} Y mod X \= 0 end} end X | {Sieve Ys} endend• The program forks a filter thread on each sieve call

Page 46: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 46

Example Call

local Xs Ys in

thread Xs = {Generate 2 100000} end

thread Ys = {Sieve Xs} end

thread for Y in Ys do {Show Y} end end

end

Filter 3 SieveFilter 2 Filter 5

7 | 11 |...

Page 47: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 47

Limitation of eager stream processing

• The producer might be much faster than the consumer

• This will produce a large intermediate stream that requires potentially unbounded memory storage

x5 x4 x3 x2 x1

producer consumer

Page 48: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 48

Solutions

There are three alternatives:

1. Play with the speed of the different threads, i.e. play with the scheduler to make the producer slower

2. Create a bounded buffer, say of size N, so that the producer waits automatically when the buffer is full

3. Use demand-driven approach, where the consumer activates the producer when it needs a new element (lazy evaluation)

The last two approaches introduce the notion of flow-control between concurrent activities (very common)

Page 49: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 49

Time

• In concurrent computation one would like to handle time

• proc {Time.delay T} – The running thread suspends for T milliseconds

• proc {Time.alarm T U} – Immediately creates its own thread, and binds U to unit after T milliseconds

Page 50: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 50

Examplelocal proc {Ping N} for I in 1..N do

{Delay 500} {Browse ping} end {Browse 'ping terminate'} end proc {Pong N} for I in 1..N do

{Delay 600} {Browse pong} end {Browse 'pong terminate'} endin .... end

local....in {Browse 'game started'} thread {Ping 1000} end thread {Pong 1000} endend

Page 51: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 51

Thread Priority and Real Time• Try to run the program using the following statement:

– {Consumer thread {Producer 5000000} end}

• Switch on the panel and observe the memory behavior of the program.

• You will quickly notice that this program does not behave well.

• The reason has to do with the asynchronous message passing. If the producer sends messages i.e. create new elements in the stream, in a faster rate than the consumer can consume, increasingly more buffering will be needed until the system starts to break down.

• One possible solution is to control experimentally the rate of thread execution so that the consumers get a larger time-slice than the producers do.

Page 52: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 52

Priorities• There are three priority levels:

high,

medium, and

low (the default)

• A priority level determines how often a runnable thread is allocated a time slice.

• In Oz, a high priority thread cannot starve a low priority one. Priority determines only how large piece of the processor-cake a thread can get.

• Each thread has a unique name. To get the name of the current thread the procedure Thread.this/1 is called.

• Having a reference to a thread, by using its name, enables operations on threads such as:

terminating a thread, or

raising an exception in a thread.

• Thread operations are defined the standard module Thread.

Page 53: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 53

Thread priority and thread control

fun {Thread.state T} %% returns thread state

proc{Thread.injectException T E} %% exception E injected into thread

fun {Thread.this} %% returns 1st class reference to thread

proc{Thread.setPriority T P} %% P is high, medium or low

proc{Thread.setThisPriority P} %% as above on current thread

fun{Property.get priorities} %% get priority ratios

proc{Property.put priorities(high:H medium:M)}

Page 54: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 54

Thread Priorities

• Oz has three priority levels. The system procedure

• {Property.put priorities p(medium:Y high:X)} Sets the processor-time ratio to X:1 between high-priority threads and medium-

priority thread.

It also sets the processor-time ratio to Y:1 between medium-priority threads and low-priority threads. X and Y are integers.

– Example:

• {Property.put priorities p(high:10 medium:10)}

• Now let us make our producer-consumer program work. We give the producer low priority, and the consumer high. We also set the priority ratios to 10:1 and 10:1.

Page 55: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 55

The program

local L in {Property.put priorities p(high:10 medium:10)} thread {Thread.setThisPriority low} L = {Producer 5000000} end thread {Thread.setThisPriority high} {Consumer L} endend

Page 56: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 56

Concurrent control abstraction

• We have seen how threads are forked by ’thread ... end’

• A natural question to ask is: how can we join threads?

fork

join

threads

Page 57: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 57

Termination detection• This is a special case of detecting termination of multiple threads, and

making another thread wait on that event.

• The general scheme is quite easy because of dataflow variables:

thread S1 X1 = unit end thread S2 X2 = X1 end ... thread Sn Xn = Xn-1 end

{Wait Xn} % Continue main thread

• When all threads terminate the variables X1 … XN will be merged together labeling a single box that contains the value unit.

• {Wait XN} suspends the main thread until XN is bound.

Page 58: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 58

Concurrent Composition

conc S1 [] S2 [] … [] Sn end

{Conc [ proc{$} S1 end proc{$} S2 end

... proc{$} Sn end] }

• Takes a single argument that is a list of nullary procedures.

• When it is executed, the procedures are forked concurrently. The next statement is executed only when all procedures in the list terminate.

Page 59: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 59

Conclocal proc {Conc1 Ps I O} case Ps of P|Pr then M in thread {P} M = I end {Conc1 Pr M O} [] nil then O = I end endin proc {Conc Ps} X in {Conc1 Ps unit X} {Wait X}

endend

This abstraction takesa list of zero-argumentprocedures and terminateafter all these threads have terminated

Page 60: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 60

Examplelocal proc {Ping N} for I in 1..N do

{Delay 500} {Browse ping} end {Browse 'ping terminate'} end proc {Pong N} for I in 1..N do

{Delay 600} {Browse pong} end {Browse 'pong terminate'} endin .... end

local....in {Browse 'game started'} {Conc

[ proc {$} {Ping 1000} end proc {$} {Pong 1000} end ]} {Browse ’game terminated’}end

Page 61: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 61

Futures• A future is a read-only capability of a single-assignment variable. For

example to create a future of the variable X we perform the operation !! to create a future Y: Y = !!X 

• A thread trying to use the value of a future, e.g. using Y, will suspend until the variable of the future, e.g. X, gets bound.

• One way to execute a procedure lazily, i.e. in a demand-driven manner, is to use the operation {ByNeed +P ?F}.

• ByNeed takes a zero-argument function P, and returns a future F. When a thread tries to access the value of F, the function {P} is called, and its result is bound to F.

• This allows us to perform demand-driven computations in a straightforward manner.

Page 62: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 62

Example

• declare Y{ByNeed fun {$} 1 end Y}{Browse Y}

• we will observe that Y becomes a future, i.e. we will see Y<Future> in the Browser.

• If we try to access the value of Y, it will get bound to 1.

• One way to access Y is by perform the operation {Wait Y} which triggers the producing procedure.

Page 63: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 63

Why not always use declarative concurrency?

• The concurrent declarative model is much simpler– Programs give the same results as if they were sequential, but they give the results

incrementally (assuming a single binder per dataflow variable)

• Why is this model so easy?– Because dataflow variables can be bound to only one value. A thread that shares a

variable with another thread does not have to worry that the other thread will change the binding.

• So why not stick with this model?– In many cases, we can stick with this model

– But not always. For example, two clients that communicate with one server cannot be programmed in this model. Why not? Because there is an observable nondeterminism.

• The concurrent declarative model is deterministic. If the program we write has an observable nondeterminism, then we cannot use the model.

Page 64: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 64

Concurrent stateful models ::= skip empty statement | x = y variable-variable binding

| x = v variable-value binding

| s1 s2 sequential composition| local x in s1 end declaration| proc { x y1 … yn } s1 end procedure creation| if x then s1 else s2 end conditional| { x y1 … yn } procedure application| case x of pattern then s1 else s2 end pattern matching| {NewName x } name creation| thread s end thread creation| {ByNeed x y } trigger creation| try s1 catch x then s2 end exception context| raise x end raise exception| {NewCell x y } cell creation| {Exchange x y z } cell exchange

Page 65: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 65

Concurrency and stateare tough when used together

• Execution consists of multiple threads, all executing independently and all using shared cells

• A thread’s execution is a sequence of Access and Assign operations (or Exchange operations)

• Because of interleaving semantics, execution happens as if there was one global order of operations

• Assume two threads and each thread does k operations. Then the total number of possible interleavings is This is exponential in k.

• One can program by reasoning on all possible interleavings, but this is extremely hard. What do we do?

2kk( )

Page 66: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 66

Programming withconcurrency and state

• Programming with concurrency and state is largely a matter of reducing the number of interleavings, so that we can reason about programs in a simpler way. There are two basic approaches: message passing and atomic actions.

• Message passing with active objects: Programs consist of threads that send asynchronous messages to each other. Each thread only receives a message when it is ready, which reduces the number of interleavings.

• Atomic actions on shared state: Programs consist of passive objects that are called by threads. We build large atomic actions (e.g., with locks, monitors, or transactions) to reduce the number of interleavings.

Page 67: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 67

When to use each approach

• Message passing: useful for multi-agent applications, i.e., programs that consist of autonomous entities (« agents », « actors » or « active objects ») that communicate with each other.

• Atomic actions: useful for data-centered applications, i.e., programs that consist of a large repository of data (« database » or « shared state ») that is accessed and updated concurrently.

• Both approaches can be used together in the same application, for different parts

Page 68: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 68

Ports and cells

• We have seen cells, the basic unit of encapsulated state, as a primitive concept underlying stateful and object-oriented programming. Cells are like variables in imperative languages.

• Cells are the natural concept for programming with shared state

• There is another way to add state to a language, which we call a port. A port is an asynchronous FIFO communication channel.

• Ports are a natural concept for programming with active objects

• Cells and ports are duals of each other– Each can be implemented with the other, so they are equal in expressiveness

– Each is more natural in some circumstances

– They are equivalent because each allows many-to-one communication (cell shared by threads, port shared by threads)

Page 69: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 69

Ports

• A port is an ADT with two operations:– {NewPort S P}: create a new port P with a new stream S. The

stream is a list with unbound tail, used to model the FIFO nature of the communications channel.

– {Send P X}: send message X on port P. The message is appended to the stream S and can be read by threads reading S.

• Example:declare P S in

{NewPort S P}{Browse S}thread{Send P 1}end

thread{Send P 2}end

Page 70: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 70

Building locks with cells• The basic way to program with shared state is by using locks

• A lock is a region of the program that can only be occupied by one thread at a time. If a second thread attempts to enter, it will suspend until the first thread exits.

• More sophisticated versions of locks are monitors and transactions:– Monitors: locks with a gating mechanism (e.g., wait/notify in Java) to control which

threads enter and exit and when. Monitors are the standard primitive for concurrent programming in Java.

– Transactions: locks that have two exits, a normal and abnormal exit. Upon abnormal exit (called « abort »), all operations performed in the lock are undone, as if they were never done. Normal exit is called « commit » .

• Locks can be built with cells. The idea is simple: the cell contains a token. A thread attempting to enter the lock takes the token. A thread that finds no token will wait until the token is put back.

Page 71: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 71

Building active objects with ports

• Here is a simple active object:

declare P inlocal Xs in

{NewPort Xs P}thread {ForAll Xs proc {$ X} {Browse X} end}

endend

{Send P foo(1)}thread {Send P bar(2)} end

Page 72: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 72

Defining ports with cells• A port is an unbundled stateful ADT:

proc {NewPort S P}C={NewCell S}

inP={Wrap C}

end

proc {Send P X}C={Unwrap P}Old

in{Exchange C X|Old Old}

end

Anyone can do a send becauseanyone can do an exchange

Page 73: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 73

Active objects with classes• An active object’s behavior can be defined by a class

• The class is used to create a (passive) object, which is invoked by one thread that reads from a port’s stream

• Anyone can send a message to the object asynchronously, and the object will execute them one after the other, in sequential fashion:

declare ActObj inlocal Obj Xs P in

Obj={New Class init}{NewPort Xs P}thread {ForAll Xs proc {$ M} {Obj M} end} endproc {ActObj M} {Send P M} end

end{ActObj msg(1)}

• Note that {Obj M} is synchronous and {ActObj M} is asynchronous!

Page 74: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 74

Creating active objectswith NewActive

• We can create a function NewActive that behaves like New except that it creates an active object:

fun {NewActive Class Init}Obj Xs P

inObj={New Class Init}{NewPort Xs P}thread {ForAll Xs proc {$ M} {Obj M} end} endproc {$ M} {Send P M} end

end

ActObj = {NewActive Class init}

Page 75: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 75

Making active objectssynchronous

• We can make an active object synchronous by using a dataflow variable to store a result, and waiting for the result before continuing

fun {NewSynchronousActive Class Init}Obj Xs P

inObj={New Class Init}{NewPort Xs P}thread {ForAll Xs proc {$ msg(M X)} {Obj M} X=unit end} endproc {$ M} X in {Send P msg(M X)} {Wait X} end

end

• This can be modified to handle when the active object raises an exception, to pass the exception back to the caller

Page 76: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 76

Playing catch

class Bounceattr other count:0meth init(Other) other:=Otherendmeth ball count:=@count+1 {@other ball}endmeth get(X) X=@countend

end

declare B1 B2 inB1={NewActive Bounce init(B2)}B2={NewActive Bounce init(B1)}

% Get the ball bouncing{B1 ball}

% Follow the bounces{Browse {B1 get($)}}

B1 B2ball

ball

Page 77: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 77

An area server

class AreaServermeth init skip endmeth square(X A) A=X*Xendmeth circle(R A) A=3.14*R*Rend

end

declare S inS={NewActive AreaServer init}

% Query the serverdeclare A in{S square(10 A)} {Browse A}

declare A in {S circle(20 A)} {Browse A}

Page 78: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 78

Event manager with active objects

• An event manager contains a set of event handlers

• Each handler is a triple Id#F#S where Id identifies it, F is the state update function, and S is the state

• Reception of an event causes all triples to be replaced by Id#F#{F E S} (transition from F to {F E S})

• The manager EM is an active object with four methods:– {EM init} initializes the event manager– {EM event(E)} posts event E at the manager– {EM add(F S Id)} adds new handler with F, S, and returns Id– {EM delete(Id S)} removed handler Id, returns state

• This example taken from real use in Erlang

Page 79: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 79

Defining the event manager

• Mix of functional and object-oriented style

class EventManager attr handlers meth init handlers:=nil end meth event(E) handlers:= {Map @handlers fun {$ Id#F#S} Id#F#{F E S} end} end meth add(F S Id) Id={NewName} handlers:=Id#F#S|@handlers end meth delete(DId DS) handlers:={List.partition @handlers fun {$ Id#F#S} DId==Id end [_#_#DS]} endend

State transition done usingfunctional programming

Page 80: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 80

Using the event manager

• Simple memory-based handler keeps list of events

declare EM MemH Id inEM={NewActive EventManager init}

MemH=fun {$ E Buf} E|Buf end{EM add(MemH nil Id)}

{EM event(a1)}{EM event(a2)}...

• An event handler is purely functional, yet when put in the event manager, the latter is a concurrent imperative program. This is an example of separation of concerns by using multiple paradigms.

Page 81: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 81

Exercises

1. VRH Exercise 4.11.3 (page 339)Compare the sequential vs concurrent execution performance of equivalent SALSA

programs.

2. VRH Exercise 4.11.5 (page 339)

3. SALSA asynchronous message passing enables to tag messages with properties: priority, delay, and waitfor. Compare these mechanisms with Oz thread priorities, time delays and alarms, and futures.

4. How do SALSA tokens relate to Oz dataflow variables and futures?

5. What is the difference between multiple thread termination detection in Oz and join blocks in SALSA?

Page 82: C. Varela; Adapted with permission from S. Haridi and P. Van Roy1 Oz, Declarative Concurrency, and Active Objects (VRH 4, 7.8) Carlos Varela RPI Adapted.

C. Varela; Adapted with permission from S. Haridi and P. Van Roy 82

Exercises

6. Do Python, Java and C++ provide a linguistic abstraction for active objects? If so, which? If not, how would you go about implementing this abstraction?

7. Exercise VRH 7.9.6(a) (pg 568)

8. Write a Producer-Consumer program in SALSA (see VRH Section 4.3.1. for specification.)