CPS 310 Threads and Concurrency: Topics Jeff Chase Duke - - PowerPoint PPT Presentation

cps 310 threads and concurrency topics
SMART_READER_LITE
LIVE PREVIEW

CPS 310 Threads and Concurrency: Topics Jeff Chase Duke - - PowerPoint PPT Presentation

D D u k e S y s t t e m s CPS 310 Threads and Concurrency: Topics Jeff Chase Duke University h>p://www.cs.duke.edu/~chase/cps310 Terminology and syntax The abstractions for


slide-1
SLIDE 1

D D u k e S y s t t e m s

CPS ¡310 ¡ Threads ¡and ¡Concurrency: ¡Topics ¡

Jeff ¡Chase ¡ Duke ¡University ¡ ¡ h>p://www.cs.duke.edu/~chase/cps310 ¡

slide-2
SLIDE 2

Terminology and syntax

  • The abstractions for concurrency control in this class are

now sort-of universal at the OS/API level.

  • Monitors (mutex+CV) with Mesa semantics appear in:

– Java (e.g., on Android) and JVM languages (e.g., Scala) – POSIX threads or Pthreads (used on Linux and MacOS/iOS) – Windows, C#/.NET, and other Microsoft systems

  • Terminology and APIs vary a bit.

– mutex == lock == Java “synchronized” – monitor == mutex + condition variable (CV) – signal() == notify(), broadcast() == notifyAll()

  • The slides use interchangeable terms interchangeably.
slide-3
SLIDE 3

Example: the soda/HFCS machine

Vending ¡machine ¡ (buffer) ¡ Soda ¡drinker ¡ (consumer) ¡ Delivery ¡person ¡ (producer) ¡

slide-4
SLIDE 4

Producer-consumer code

producer () { add one soda to machine } consumer () { take a soda from machine }

slide-5
SLIDE 5

Solving producer-consumer

1. What are the variables/shared state? ê Soda machine buffer ê Number of sodas in machine (≤ MaxSodas) 2. Locks? ê 1 to protect all shared state (sodaLock) 3. Mutual exclusion? ê Only one thread can manipulate machine at a time 4. Ordering constraints? ê Consumer must wait if machine is empty (CV hasSoda) ê Producer must wait if machine is full (CV hasRoom)

slide-6
SLIDE 6

Producer-consumer code

producer () { lock add one soda to machine unlock } consumer () { lock take a soda from machine unlock }

slide-7
SLIDE 7

Producer-consumer code

producer () { lock wait if full add one soda to machine notify (not empty) unlock } consumer () { lock wait if empty take a soda from machine notify (not full) unlock }

slide-8
SLIDE 8

Producer-consumer code

producer () { lock (sodaLock) while(numSodas==MaxSodas){ wait (sodaLock, hasRoom) } add one soda to machine signal (hasSoda) unlock (sodaLock) } consumer () { lock (sodaLock) while (numSodas == 0) { wait (sodaLock,hasSoda) } take a soda from machine signal (hasRoom) unlock (sodaLock) } Mx ¡ CV1 ¡ Mx ¡ CV2 ¡ CV1 ¡ CV2 ¡

slide-9
SLIDE 9

Producer-consumer code

synchronized producer () { while(numSodas==maxSodas) { wait () } put a soda from machine notify(); } synchronized consumer () { while (numSodas == 0) {

  • .wait ()

} take a soda from machine notify(); }

slide-10
SLIDE 10

Producer-consumer code

consumer () { synchronized(o) { while (numSodas == 0) {

  • .wait ()

} take a soda

  • .notify();

} }

slide-11
SLIDE 11

Producer-consumer code

producer () { lock (sodaLock) while(numSodas==MaxSodas){ wait (sodaLock, hasRoom) } fill machine with soda broadcast(hasSoda) unlock (sodaLock) } consumer () { lock (sodaLock) while (numSodas == 0) { wait (sodaLock,hasSoda) } take a soda from machine signal(hasRoom) unlock (sodaLock) }

The ¡signal ¡should ¡be ¡a ¡broadcast ¡if ¡the ¡producer ¡can ¡produce ¡ more ¡than ¡one ¡resource, ¡and ¡there ¡are ¡mulOple ¡consumers. ¡ ¡

lpcox slide edited by chase

slide-12
SLIDE 12

Variations: looping producer

ê Producer

ê Infinite loop ok? ê Why/why not?

ê Release lock in wait call

producer () { lock (sodaLock) while (1) { while(numSodas==MaxSodas){ wait (sodaLock, hasRoom) } add soda to machine signal (hasSoda) } unlock (sodaLock) }

slide-13
SLIDE 13

Variations: resting producer

ê Producer

ê Sleep ok? ê Why/why not?

ê Shouldn’t hold locks during a slow operation

producer () { lock (sodaLock) while (1) { sleep (1 hour) while(numSodas==MaxSodas){ wait (sodaLock, hasRoom) } add soda to machine signal (hasSoda)

}

unlock (sodaLock) }

slide-14
SLIDE 14

Variations: one CV?

producer () { lock (sodaLock) while(numSodas==MaxSodas){ wait (sodaLock,hasRorS) } add one soda to machine signal(hasRorS) unlock (sodaLock) } consumer () { lock (sodaLock) while (numSodas == 0) { wait (sodaLock,hasRorS) } take a soda from machine signal (hasRorS) unlock (sodaLock) }

Two ¡producers, ¡two ¡consumers: ¡who ¡consumes ¡a ¡signal? ¡ ¡ ProducerA ¡and ¡ConsumerB ¡wait ¡while ¡ConsumerC ¡signals? ¡

Mx ¡ CV ¡ Mx ¡ CV ¡ CV ¡ CV ¡

slide-15
SLIDE 15

Variations: one CV?

producer () { lock (sodaLock) while(numSodas==MaxSodas){ wait (sodaLock,hasRorS) } add one soda to machine signal (hasRorS) unlock (sodaLock) } consumer () { lock (sodaLock) while (numSodas == 0) { wait (sodaLock,hasRorS) } take a soda from machine signal (hasRorS) unlock (sodaLock) }

Is ¡it ¡possible ¡to ¡have ¡a ¡producer ¡and ¡consumer ¡both ¡waiOng? ¡ ¡ ¡max=1, ¡cA ¡and ¡cB ¡wait, ¡pC ¡adds/signals, ¡pD ¡waits, ¡cA ¡wakes ¡

slide-16
SLIDE 16

Variations: one CV?

producer () { lock (sodaLock) while(numSodas==MaxSodas){ wait (sodaLock,hasRorS) } add one soda to machine signal (hasRorS) unlock (sodaLock) } consumer () { lock (sodaLock) while (numSodas == 0) { wait (sodaLock,hasRorS) } take a soda from machine signal (hasRorS) unlock (sodaLock) }

How ¡can ¡we ¡make ¡the ¡one ¡CV ¡soluOon ¡work? ¡

slide-17
SLIDE 17

Variations: one CV?

producer () { lock (sodaLock) while(numSodas==MaxSodas){ wait (sodaLock,hasRorS) } add one soda to machine broadcast (hasRorS) unlock (sodaLock) } consumer () { lock (sodaLock) while (numSodas == 0) { wait (sodaLock,hasRorS) } take a soda from machine broadcast (hasRorS) unlock (sodaLock) }

Use ¡broadcast ¡instead ¡of ¡signal: ¡safe ¡but ¡slow. ¡

slide-18
SLIDE 18

Broadcast vs signal

ê Can I always use broadcast instead of signal?

ê Yes, assuming threads recheck condition ê And they should: “loop before you leap”! ê Mesa semantics requires it anyway: another thread could get to the lock before wait returns.

ê Why might I use signal instead?

ê Efficiency (spurious wakeups) ê May wakeup threads for no good reason ê “Signal is just a performance hint”.

lpcox slide edited by chase

slide-19
SLIDE 19

Locking a critical section

mx->Acquire(); x = x + 1; mx->Release(); mx->Acquire(); x = x + 1; mx->Release();

load add store load add store

Holding a shared mutex prevents competing threads from entering a critical section protected by the shared mutex (monitor). At most

  • ne thread runs in the critical section at a time.

A A R R

þ þ

The threads may run the critical section in either order, but the schedule can never enter the grey region where both threads execute the section at the same time.

x=x+1 x=x+1

slide-20
SLIDE 20

Locking a critical section

mx->Acquire(); x = x + 1; mx->Release(); mx->Acquire(); x = x + 1; mx->Release();

load add store load add store load add store load add store load add store load add store

3. 4.

Holding a shared mutex prevents competing threads from entering a critical section. If the critical section code acquires the mutex, then its execution is serialized: only one thread runs it at a time.

þ þ þ þ

synchronized serialized atomic

slide-21
SLIDE 21

How about this?

x = x + 1; mx->Acquire(); x = x + 1; mx->Release();

load add store load add store

A B

slide-22
SLIDE 22

How about this?

x = x + 1; mx->Acquire(); x = x + 1; mx->Release();

load add store load add store

The locking discipline is not followed: purple fails to acquire the lock mx. Or rather: purple accesses the variable x through another program section A that is mutually critical with B, but does not acquire the mutex. A locking scheme is a convention that the entire program must follow.

A B

slide-23
SLIDE 23

How about this?

mx->Acquire(); x = x + 1; mx->Release();

load add store load add store

B lock->Acquire(); x = x + 1; lock->Release(); A

slide-24
SLIDE 24

How about this?

mx->Acquire(); x = x + 1; mx->Release();

load add store load add store

This guy is not acquiring the right lock. Or whatever. They’re not using the same lock, and that’s what matters. A locking scheme is a convention that the entire program must follow.

B lock->Acquire(); x = x + 1; lock->Release(); A

slide-25
SLIDE 25

Ucontext library routines

  • The system can use ucontext routines to:

– “Freeze” at a point in time of the execution – Restart execution from a frozen moment in time – Execution continues where it left off…if the memory state is right.

  • The system can implement multiple independent threads
  • f execution within the same address space.

– Create a context for a new thread with makecontext: when switched in it will call a specified procedure with specified arg. – Modify saved contexts at will. – Context switch with swapcontext: transfer a core from one thread to another

slide-26
SLIDE 26

Messing with the context

#include <ucontext.h> int count = 0; ucontext_t context; int main() { int i = 0; getcontext(&context); count += 1; i += 1; sleep(2); printf(”…", count, i); setcontext(&context); } ucontext Standard C library routines to: Save current register context to a block

  • f memory (getcontext from core)

Load/restore current register context from a block of memory (setcontext) Also: makecontext, swapcontext Details of the saved context (ucontext_t structure) are machine-dependent.

slide-27
SLIDE 27

Messing with the context (2)

#include <ucontext.h> int count = 0; ucontext_t context; int main() { int i = 0; getcontext(&context); count += 1; i += 1; sleep(1); printf(”…", count, i); setcontext(&context); } Loading the saved context transfers control to this block

  • f code. (Why?)

What about the stack?

Save CPU core context to memory Load core context from memory

slide-28
SLIDE 28

Messing with the context (3)

#include <ucontext.h> int count = 0; ucontext_t context; int main() { int i = 0; getcontext(&context); count += 1; i += 1; sleep(1); printf(”…", count, i); setcontext(&context); } chase$ cc -o context0 context0.c < warnings: ucontext deprecated on MacOS > chase$ ./context0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 …

slide-29
SLIDE 29

Reading behind the C

Disassembled code: movl 0x0000017a(%rip),%ecx addl $0x00000001,%ecx movl %ecx,0x0000016e(%rip) movl 0xfc(%rbp),%ecx addl $0x00000001,%ecx movl %ecx,0xfc(%rbp) %rip and %rbp are set “right”, then these references “work”.

count += 1; i += 1;

On MacOS: chase$ man otool chase$ otool –vt context0 … On this machine, with this cc: Static global _count is addressed relative to the location of the code itself, as given by the PC register [%rip is instruction pointer register] Local variable i is addressed as an

  • ffset from stack frame.

[%rbp is stack frame base pointer]

slide-30
SLIDE 30

Messing with the context (4)

#include <ucontext.h> int count = 0; ucontext_t context; int main() { int i = 0; getcontext(&context); count += 1; i += 1; sleep(1); printf(”…", count, i); setcontext(&context); } chase$ cc –O2 -o context0 context0.c < warnings: ucontext deprecated on MacOS > chase$ ./context0 1 1 2 1 3 1 4 1 5 1 6 1 7 1 … What happened?

slide-31
SLIDE 31

Messing with the context (5)

#include <ucontext.h> int count = 0; ucontext_t context; int main() { int i = 0; getcontext(&context); count += 1; i += 1; sleep(1); printf(”…", count, i); setcontext(&context); }

What does this do?