Title: Synchronization
1Synchronization
- Threads communicate to ensure consistency
- If not race condition(non-deterministic result)
- Accomplished by synchronization operations
- How to write concurrent code
- How to implement synchronization operations
2Synchronization Motivation
- The too much milk problem
- Model of need to synchronize activities
3Synchronization Terminology
- Mutual exclusion (mutex) prevents multiple
threads from entering - Critical section regions of code that modify
or access shared variables - code only one thread can execute at a time
- Lock mechanism for mutual exclusion
- Lock on entering critical section, accessing
shared data - Unlock when complete
- Wait if locked
4Solving the Too Much Milk Problem
- Correctness properties
- Safety nothing bad happens
- Progress something good eventually happens
- First use atomic loads stores as building
blocks - Leave a note (lock)
- Remove a note (unlock)
- Dont buy milk if theres a note (wait)
5Too Much Milk Solution 1
- thread A
- if (no milk no note)
- leave note
- buy milk
- remove note
thread B if (no milk no note) leave note
buy milk remove note
6Too Much Milk Solution 2
Idea use labeled notes
- thread A
- leave note A
- if (no note B)
- if (no milk)
- buy milk
- remove note A
thread B leave note B if (no note A) if (no
milk) buy milk remove note B
7Language Support
- Synchronization complicated
- Better way provide language-level support
- Higher-level approach
- Hide gory details in runtime system
- Increasingly high-level approaches
- Locks, Atomic Operations
- Semaphores generalized locks
- Monitors tie shared data to synchronization
8Locks
- Provide mutual exclusion to shared data via two
atomic routines - LockAcquire wait for lock, then take it
- LockRelease unlock, wake up waiters
- Rules
- Acquire lock before accessing shared data
- Release lock afterwards
- Lock initially released
9Too Much Milk Locks
thread A Lock.acquire() if (no milk) buy
milk Lock.release()
thread B Lock.acquire() if (no milk) buy
milk Lock.release()
- Clean, symmetric - but how do we implement it?
10Implementing Locks
- Requires hardware support (in general)
- Can build on atomic operations
- Disable interrupts
- Uniprocessors only
- Test Set, Compare Swap
11Disabling Interrupts
- Prevent scheduler from switching threads in
middle of critical sections - Ignores quantum expiration (timer interrupt)
- No handling I/O operations
- (Dont make I/O calls in critical section!)
- To ensure current sequence of instructions run
atomically - Drawback?
12Atomic Read-Write-Modify Instructions
- Atomically read old value, write new value
- Examples
- Test Set (most arch)
- Exchange (x86)
- Compare Swap (68K, Sparc)
13Implementing Locks Test Set
- int testset (int value)
- int old value
- value 1
- return old
class Lock private int value Lock()
value 0 void acquire() void
release()
pseudo-code red atomic
- Effect of testset(value) when value 0? or 1?
- acquire wait until lock released, then take it
- release release lock
- value 1 (locked) 0 (unlocked)
void acquire() while (testset(value))
void release() value 0
14Busy Waiting (Spinning)
- Whats wrong with this implementation?
- CPU utilization?
- Different priorities?
void acquire() while (testset(value))
void release() value 0
spin-lock
15Minimizing Busy Waiting
- Cant implement locks with test set without any
waiting (w/o disabling interrupts) - Add queue to lock and sleep blocking lock
void acquire() while (1) if
(testset(value)) put thread on queue,
sleep else break
void release() value 0 wake up
threads
16Locks in POSIX
pthread_mutex_t my_mutex pthread_mutex_init(my_m
utex) // also pthread_mutex_t my_mutex
PTHREAD_MUTEX_INITIALIZER ... pthread_mutex_lock(
my_mutex) x x 1 / This is the critical
section / pthread_mutex_unlock(my_mutex) ... pt
hread_mutex_destroy(my_mutex)
17Locks in POSIX Example
- Reader-writer problem
- Share a buffer which holds one item (an integer)
- A single reader and writer
- Reader only read when there is item in buffer
- Writer only write when there is space in buffer
18Locks in POSIX Example - II
include ltpthread.hgt include ltstdio.hgt int
buffer_has_item 0 int buffer pthread_mutex_t
mutex void reader_function(void )
while(1) pthread_mutex_lock( mutex )
if ( buffer_has_item 1) printf("reader
consumes one item d.\n", buffer)
buffer_has_item 0 pthread_mutex_unloc
k( mutex )
19Locks in POSIX Example - III
void writer_function(void) while(1)
pthread_mutex_lock( mutex ) if (
buffer_has_item 0 ) buffer
rand()100 printf("writer produces one
item d\n", buffer) buffer_has_item
1 pthread_mutex_unlock( mutex )
void main() pthread_t reader
pthread_mutex_init(mutex, NULL)
pthread_create( reader, NULL, reader_function,NUL
L) writer_function()
20Locks as Synch Primitive
- Locks provide mutual exclusion
- Only one thread enters section at a time
- Simplifies writing concurrent programs
- Low-level
- Can complicate coding
- e.g., allow at most n threads to access
resource - What are some alternatives?
21Locks Semaphores
- Implementing locks
- Semaphores
- Monitors
22Semaphores
- Whats a semaphore anyway?
- A visual system for sending information by means
of two flags that are held one in each hand,
using an alphabetic code based on the position of
the signaler's arms.
23Semaphores
- Whats a semaphore anyway?
A visual signaling apparatus with flags, lights,
or mechanically moving arms.
24Semaphores in CS
- Computer science Dijkstra (1965)
A non-negative integer counter with atomic
increment decrement. Blocks rather than going
negative.
- Higher-level than locks but not too high level
25Semaphores Key Concepts
- P(sem), a.k.a. wait decrement counter
- If sem 0, block until greater than zero
- P prolagen (proberen te verlagen, try to
decrease)
- V(sem), a.k.a. signal increment counter
- Wake 1 waiting process
- V verhogen (increase)
In Holland the good Dr. Dijkstra Took a break
from a walk on his bijkstra And said "Which
shall it be? Take a P or a V? For the two seem to
me quite alijkstra!"
26Implementing Semaphores
class Semaphore private int value private
Queue q Semaphore(int v) value v
void wait() if (value gt 0) value
value 1 if (value 0) add this
process to Q sleep()
void signal() value value 1 if (anyone
on Q) remove P from Q wakeup(P)
27Variants of Semaphores
- Binary semaphore
- just two values (0 or 1), typically initial value
1 (free) - Counting semaphore
- useful when units of resource are available
- initialized to number of resources
- thread can access as long as one unit available
28Binary Semaphores Example
thread A Lock.acquire() if (no milk) buy
milk Lock.release()
thread B Lock.acquire() if (no milk) buy
milk Lock.release()
thread A sem.wait() if (no milk) buy
milk sem.signal()
thread B sem.wait() if (no milk) buy
milk sem.signal()
- too much milk with binary semaphores(initially
1)
29Binary Semaphore Example
- More flexible than locks!
- By initializing semaphore to 0,threads can wait
for an event to occur
thread A // wait for thread B sem.wait() // do
stuff
thread B // do stuff, then // wake up
A sem.signal()
30Counting Semaphores Example
- Controlling resources
- Allow threads to use at most 5 files
simultaneously - Initialize to 5
thread A sem.wait() // use a file sem.signal()
thread B sem.wait() // use a file sem.signal()
31Semaphore in POSIX
sem_t sem sem_init(sem, int pshared, unsigned
value) sem_wait(sem) sem_post(sem) sem_dest
roy(sem)
32Solving Reader-writer Problem Using Semaphore
- Reader-writer problem
- Share a buffer which holds one item (an integer)
- A single reader and writer
- Reader only read when there is item in buffer
- Writer only write when there is space in buffer
- How to make the writer enter the critical section
first? - How to make the reader writer alternate to
enter critical section?
33Semaphore in Linux Solving Reader-writer Problem
include ltstdio.hgt include ltstdlib.hgt include
ltpthread.hgt include ltsemaphore.hgt sem_t
readers_turn, writers_turn / semaphore
declaration / int buffer, loopcnt 5 void
reader_function(void ) int i for
(i0 iltloopcnt i) sem_wait(
readers_turn ) printf("reader consumes
one item d.\n", buffer) sem_post(
writers_turn ) void
writer_function(void) int i for (i0
iltloopcnt i) sem_wait( writers_turn
) buffer rand() printf("writer
produces one item d\n", buffer)
sem_post( readers_turn )
34Semaphore in Linux Solving Reader-writer Problem
- II
int main() pthread_t reader if
(sem_init(readers_turn, 0, 0) lt 0)
perror("sem_init") exit(1) if
(sem_init(writers_turn, 0, 1) lt 0)
perror("sem_init") exit(1)
if(pthread_create( reader, NULL,
reader_function, NULL) ! 0)
perror("pthread_create") exit(1)
writer_function() sem_destroy(readers
_turn) sem_destroy(writers_turn)
return 1
35Summary
- Implementing locks
- Test Set
- Spin locks, blocking locks
- Semaphores
- Generalization of locks
- Binary, counting (Dijkstra-style)
- Useful for
- Controlling resources
- Waiting on events