The Language of Concurrency

prettybadelyngeSoftware and s/w Development

Nov 18, 2013 (3 years and 6 months ago)

69 views

Threads
,
Races,
Message Passing, Sequential Consistency

By Bartosz Milewski


The Language of Concurrency

The Plan


Processes vs. Threads


Multithreading vs. Parallelization


Shared Memory vs. Message Passing


Data Races and Atomicity Violations


Relaxed Memory Models


Sequential Consistency and DRF Guarantee


Risks of Concurrency


Debugging Concurrent Programs

Page


2

Sequential vs. Concurrent


Sequential execution


O
ne action follows another


The effects of previous action
visible to next action

Page


3

while
(1
)

{


tmp

=
stk
-
>top;


node
-
>next =
tmp
;




if(
stk
-
>
top ==
tmp
)


{


stk
-
>
top =
node
;





break
;


}

}


while
(1
)

{
tmp

=
stk
-
>top;


node
-
>next =
tmp
;







if(
stk
-
>
top ==
tmp
)


{



stk
-
>
top =
node;


break
;


}

}


Concurrent execution


No specific order of execution for programs or parts of programs


Conceptually (or actually) multiple actions executing at the same time


Effects of actions may be visible to other concurrent actions out of
order

while
(1
)

{
tmp

=
stk
-
>top;


node
-
>next =
tmp
;





if(
stk
-
>
top ==
tmp
)


{


stk
-
>
top =
node
;





break
;


}

}

Memory Sharing


Processes


Each has a separate address space


Communication only through special channels


Messages, sockets, (memory
-
mapped) files


Threads


S
hare the same address space within process


Can read and write to the same memory address


Usually local (stack) variables are considered thread
-
private


Beware of closures


Concurrent memory access leads to collisions


Page


4

Threads vs. Tasks


Multithreading


Explicit thread creation and work assignment


Improves latency


Doesn’t scale well


Parallelization


Partitioning of work for parallel execution: tasks


The system, runtime, or library, assigns tasks to threads


Improves performance, if done correctly


Scales better with the number of cores


Page


5

Communication Between
T
hreads


Shared memory


Threads accessing
concurrently

shared variables/objects


If not synchronized, leads to data races, atomicity violations


Synchronization through locks/atomic variables


Message passing


No sharing of memory (hard to enforce between threads)


Message Queues, Channels, Mailboxes, Actors


Scales up to inter
-
process and distributed communications (marshaling)


But usually slower that sharing


Within a process, possibility of passing references through messages


Unintended sharing, races

Page


6

Races


Conflict


Two or more threads accessing the same memory location, at least one of them
writing (others may be reading or writing)


Data Race


A conflict with no intervening synchronization


In most cases synchronization by locking (Java synchronized)


Lock both reads and writes with the same lock


Atomic variables (Java volatile) used in lock
-
free programming


To be left to gurus

Page


7

// Thread 1

ready

= true;

// Thread 2

if (
ready
)



...;

Atomicity Violations


Updating more than one location


Momentarily breaking an invariant (inconsistent state)


Lock the related locations with one lock


For the whole duration of access (both read and write)


Reading and modifying the same location


Page


8

// Class
SinglyLinkedList

with member
head

synchronized

void
AddLink
() {



Node
node

= new Node();



node.setNext
(
head
);



head
.setNext
(node);

}

Relaxed Memory Models


Processors don’t have a consistent view of memory


Each processor has a local cache


Relaxed guarantees about write propagation between caches


Special instructions (LOCK, memory fences) to control consistency


Reflected in modern languages


Atomic variables (Java volatile)


Atomic operations (
intrinsics
)


Fences


Higher synchronization primitives (locks, etc.) built on top


Page


9

Sequential Consistency


The way we instinctively reason
about threads


Interleaving of threads: potentially
different for each execution


What value does a read see?


The last value written by any thread


“Last” in a particular interleaving


Enough to consider all possible
interleaving to prove correctness

Page


10

while
(1
) {



tmp

=
stk
-
>top;


node
-
>next =
tmp
;







if(
stk
-
>
top ==
tmp
) {


stk
-
>
top =
node
;









break
;


}

}





while
(1
) {



tmp

=
stk
-
>top;


node
-
>next =
tmp
;






if(
stk
-
>
top ==
tmp
) {



stk
-
>
top =
node;


break
;


}

}

DRF Guarantee


Modern multicores/languages break sequential consistency


The usual reasoning about threads fails

Page


11


The DRF guarantee


A data race free program is sequentially consistent


True in C++, as long as no “weak atomics”

Initially: x = 0, y = 0


Core1 Core2

x = 1 y = 1

r
1 = y r2 = x


Possible outcome: r1 == 0, r2 == 0

(on x86!)

Risks of Concurrency


Exponential number of interleavings


Hard to reason about


Hard to test exhaustively


Concurrency bugs (races, atomicity violations, deadlocks)


Hard to find


Hard to reproduce


Hard to find the cause

Page


12

Testing and Debugging


Static
analysis:


Requires complete source code


Lots
of false positives,


C
an
only reason about known primitives


Dynamic:


Slows down execution


Poor coverage


Corensic’s

Jinx


Slowdown manageable (analysis at random intervals)


Coverage of “interesting” subset of executions

Page


13

Page


14

Do You Have

Any Questions?


Resources


blog.corensic.com


More about Jinx


c
orensic.com