Outline 0024 Spring 2010 13 :: 2 CSP: communicating sequential - - PowerPoint PPT Presentation
Outline 0024 Spring 2010 13 :: 2 CSP: communicating sequential - - PowerPoint PPT Presentation
Outline 0024 Spring 2010 13 :: 2 CSP: communicating sequential processes Synchronous communication with global channels Channels are the only state that is shared between threads No shared objects, shared
– 13 :: 2 – 0024 Spring 2010
Outline
– 13 :: 3 – 0024 Spring 2010
CSP: communicating sequential processes
Synchronous communication with global channels
Channels are the only state that is shared between threads
No shared objects, shared references, or shared arrays
Only data can be transmitted -- no point to send a reference
Restrictive?
Yes and no.
Too restrictive to write interesting programs?
– 13 :: 4 – 0024 Spring 2010
More details
Unit of transfer (“message”)
Basic model: integers (int) Could be any object
What should the consumer do if there is no message?
Wait.
What should the producer do if the consumer is not ready to receive a message?
Wait.
Should messages be buffered?
No. But well see in a few minutes how to create buffers
– 13 :: 5 – 0024 Spring 2010
Operations
Many options for syntax. Send: output data into channel ch
ch ! data ch <- data ch.write(data) put(ch, data)
Receive: get data from channel (“input”)
ch ? data ch -> data data = ch.read() get(ch, data)
– 13 :: 6 – 0024 Spring 2010
Operations, continued
Think of “data” as an int or another useful object
The type/class must be known
Extensions (in the literature): get(ch, data1, data2, .., dataN) Sometimes you want to use a “signal”
Could be incoded in an integer
Example: consumer can ask for more data (signal_1) or stop the
stream (signal_2)
Better to be explicit: give it a name Need mechanism to send and receive a signal
– 13 :: 7 – 0024 Spring 2010
Operations, continued
get(ch, signal_X()) // also written as ch?signal_X() From channel ch, input the signal “signal_X”. Refuse to input any other signal
Example (in a producer): get(ch_from_consumer, more())
put(ch, signal_Y()) // also written as ch!signal_Y() Send signal “signal_Y” to ch get(ch[j], x) -- on channel ch[j] read x
– 13 :: 8 – 0024 Spring 2010
Why bother?
Coupling of data exchange (between threads) and synchronization
Synchronization implied - only when data are ready (to
receive) or can be sent
One mechanism to deal with Input/output are well-understood (and necessary)
By extending get/put to communicate with another thread we
can deal with multiple threads
No need to pick one from a long list of synchronization primitives
Semaphores? Monitors? Event queues? Many others that we have not covered in class
– 13 :: 9 – 0024 Spring 2010
Why bother in this class?
Proofs are simpler, reasoning is simpler
No need to deal with temporal logic: receiving an item
implies synchronization
Not all systems support shared objects/references
Java works fine for a multi-processor/multi-core system --
but what if you want to run on a workstation cluster ?
.. on a cloud ? .. on a collection of volunteered home computers
CSP is a model for (simple) message passing
CSP really a model - must be embedded in a programming
language
There is a Java package
Ill deviate from the Java syntax for now a little
Model to deal with streams
– 13 :: 10 – 0024 Spring 2010
Sequencing of communication
Recall: all variable names are local, ch1, ch2 connect A & B
Thread A: . . Get(ch1, x) -- wait for B .
- - transfer happens, x now 6
. Put(ch2, 2*x) -- wait for B .
- - transfer happens
Thread B: y = 5; . . Put(ch1, y+1)
- - transfer happens
. . Get(ch2, y)
- - transfer happens
– 13 :: 11 – 0024 Spring 2010
Broadcasting
Problem: take input stream and forward the data items to two “worker” threads
in, out1, out2: channels
Thread B: while ( true ) { get(in, item); put(out1, item); put(out2, item); }
– 13 :: 12 – 0024 Spring 2010
Experiment
Write a simple CSP program to perform a “join” of two streams: take input from two input channels and forward it to an output channel. Forward as fast as possible, you must maintain relative order (as defined by each channel) but the inputs from both channels can be mixed freely.
– 13 :: 13 – 0024 Spring 2010
Problems?
What would you like to add to CSP to make it possible to write such a program? What is the source of the problem?
– 13 :: 14 – 0024 Spring 2010
Guarded commands
A guarded command consists of a guard and a block (of statements)
- guard -> { block }
If the guard evaluates to true, the statement block is executed. Guards can contain only logical expressions without side effects
Input is allowed
One input operation, must be the last of a logical AND
Output is not allowed
– 13 :: 15 – 0024 Spring 2010
Guarded commands
Can be grouped in a select clause select {
- guard1 -> {block1};
- guard2 -> {block2};
…. } Any guard that evaluates to true may trigger execution
- f its block.
Only one block (of those with a guard that evaluates to true) is executed.
Select fails if no guard evaluates to true
– 13 :: 16 – 0024 Spring 2010
Failure and waiting
select {
- (j < 10) -> {j --;};
(get(ch, x)) -> {j = x;}; } If the thread that provides input to “ch” terminates (or has terminated) the corresponding “get” operation fails. Otherwise get waits for input. Same for put.
– 13 :: 17 – 0024 Spring 2010
Looping
while select { guarded command 1; guarded command 2; guarded command n; } Loop until all guarded commands fail.
– 13 :: 18 – 0024 Spring 2010
Solution (one)
Write a simple CSP program to perform a “join” of two streams: take input from two input channels and forward it to an output channel. Forward as fast as possible, you must maintain relative order (as defined by each channel) but the inputs from both channels can be mixed freely. while select { get(in1, x) -> put(out, x); get(in2, x) -> put(out, x); }; Terminates if both sources for in1 and in2 have terminated.
– 13 :: 19 – 0024 Spring 2010
Non-determinism
– 13 :: 20 – 0024 Spring 2010
Comments
– 13 :: 21 – 0024 Spring 2010
Division with remainder
Construct a program that accepts a positive dividend and divisor and returns their integer quotient and remainder.
– 13 :: 22 – 0024 Spring 2010
int x, y; while select { get(in, x, y) -> { int q, r; q = 0; r = x;
- while select {
- (r y) -> { r = r - y; q++; };
- };
- put(out, q, r);
}; };
– 13 :: 23 – 0024 Spring 2010
Buffering
CSP provides synchronous communication. If you need buffering you must introduce a separate buffer thread.
Buffer is bounded
Problem: write the program for a buffer thread.
Assume we want to buffer up to 10 integers Buffer takes input as it is generated by “producer” Passes on buffered data as requested by “consumer”
Consumer send signal “more” if it is ready for input
– 13 :: 24 – 0024 Spring 2010
We need a buffer array, counters for input and output, plus three channels to connect to.
source: producer; sink: consumer; request: consumer
signals its ready for data int [ ] buffer = new int[10]; int count_in = 0; int count_out = 0; while select { ((count_in < count_out+10) (get(source,buffer[count_in mod 10]))) -> { count_in++;}; ((count_out < count_in) (get(request,more())) -> {
- put(sink, buffer[count_out mod 10]; count_out++; } ;
};
– 13 :: 25 – 0024 Spring 2010
Why (hidden) buffering is bad
Programs can fail in unexpected ways if they assume an infinite buffer
No buffer is infinite Different systems may approximate the infinite buffer
differently
Problem: a producer sends a set (sequence) of data to a consumer. Then – using a different channel – it sends the size of the set. The consumer reads the size of the set to determine how many items to retrieve.
– 13 :: 26 – 0024 Spring 2010
Implementation strategy
– 13 :: 27 – 0024 Spring 2010
Simple solution
Thread Producer: int [ ] b; int j = 0; while (cond) { put(ch1, b[j++]); } put(ch2, j); Thread Consumer: int j=0; int count; int [ ] b; get(ch2, count); while (j<count){ get(ch1,b[j++]); }
– 13 :: 28 – 0024 Spring 2010
Real solution
– 13 :: 29 – 0024 Spring 2010
Snapshot
P
- C
- Read so far
- Filled up to
here
– 13 :: 30 – 0024 Spring 2010
Real solution 2
– 13 :: 31 – 0024 Spring 2010
Snapshot
P
- C
- Thread Producer:
int [ ] b; int j = 0; while (cond) { put(ch1, b[j++]); } put(ch2, j); Thread Consumer: int j=0; int count; int [ ] b; get(ch2, count); while (j<count){ get(ch1,b[j++]); }
– 13 :: 32 – 0024 Spring 2010
Bad situation
– 13 :: 33 – 0024 Spring 2010
Snapshot
P
- C
- Thread Producer:
int [ ] b; int j = 0; while (cond) { put(ch1, b[j++]); } put(ch2, j); Thread Consumer: int j=0; int count; int [ ] b; get(ch2, count); while (j<count){ get(ch1,b[j++]); }
– 13 :: 34 – 0024 Spring 2010
Synchronous send/receive
– 13 :: 35 – 0024 Spring 2010
Note
Thread Producer: int [ ] b; int j = 0; while (cond) { put(ch1, b[j++]); } put(ch2, j); Thread Consumer: int j=0; int count; int [ ] b; get(ch2, count); while (j<count){ get(ch1,b[j++]); }
– 13 :: 36 – 0024 Spring 2010
Channels
To simplify many programming problems, we allow multiple “writers” for a channel.
May even substitute the name of a channel by the name of a
thread.
– 13 :: 37 – 0024 Spring 2010
Semaphores
Implement an integer semaphore shared by an array of threads (with X[0 … 99] the channels to the threads) Call your signals p() and v(). Then (if we call the semaphore thread S), all other threads can use put(S, p()) and put(S, v()) to increment/decrement the semaphore.
– 13 :: 38 – 0024 Spring 2010
Semaphores
Implement an integer semaphore shared by an array of threads (with X[0 … 99] the channels to the threads). Decrementing the semaphore must be delayed if the value is not positive. int val = 0; while select { get(X[j], v()) -> { val++; } ; ((val > 0) (get(X[j], p())) -> { val--;}; }; Need to limit the range of values for j
– 13 :: 39 – 0024 Spring 2010
Semaphores
(j:low_range .. high_range): shorthand for replication int val = 0; while select { (j:0..99): get(X[j], v()) -> { val++; } ; (j:0..99): ((val > 0) (get(X[j], p())) -> { val--;}; };
– 13 :: 40 – 0024 Spring 2010
Dining philosophers
Can you come up with a CSP solution to this problem?
– 13 :: 41 – 0024 Spring 2010
Other examples
– 13 :: 42 – 0024 Spring 2010
Added bonus
Message passing programs sometimes adapt well to hierarchical memory systems
A message is received … as a result it is in memory (close to
the core that wants to receive it)
Any transfer cost (from a different core/memory region) has
been paid for
The message is processed … it helps that it is in memory
nearby.
After a message is produced, the memory cannot be
recycled before it is sure that the message has reached its destination.
Of course, in real system your mileage may vary.
Not all problems map well into message passing Not all systems support efficient message passing