Professional Documents
Culture Documents
05a Ch05 Syncrhonization v.9
05a Ch05 Syncrhonization v.9
Synchronization
Chapter 5: Process Synchronization
● Background
● The Critical-Section Problem
● Mutex Locks
● Semaphores: what are they and how are they implemented
● Classic Problems of Synchronization
● Synchronization Examples in C and Java
No coming back
until successful
Blocking or non-blocking
operations
Blocking Non-Blocking
Suspension
bool put(int value)
Busy
(O/S)
bool indicates
void put(int value) success or failure
On return –
always successful
© Dr Yigal Hoffner 17 June 2020 7
Producer-Consumer: Single Cell buffer solution
Producer Consumer
Flag
● Shared data
#define BUFFER_SIZE 10 BUFFER_SIZE-1 y z
0 1
typedef struct {
2
. . .
buffer
} item;
item buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
Producer Consumer
Process/Thread Process/Thread
● counter++ could be implemented as ● counter-- could be implemented as
Shared
Data
Structure
2. Progress - If no process is executing in its critical section and there exist some
processes that wish to enter their critical section, then the selection of the
processes that will enter the critical section next cannot be postponed
indefinitely Liveness
(deadlock free)
3. Bounded Waiting - A bound must exist on the number of times that other
processes are allowed to enter their critical sections after a process has made a
request to enter its critical section and before that request is granted
Liveness
(starvation free
& fairness)
● Many systems provide hardware support for implementing the critical section code.
● All solutions below based on idea of locking
● Protecting critical regions via locks
● Uniprocessors – could disable interrupts
● Currently running code would execute without preemption
● Does not work on multiprocessor systems
● Modern machines provide special atomic hardware instructions
4 Atomic = non-interruptible
● Either test memory word and set value
● Or swap contents of two memory words
do {
acquire lock
critical section
release lock
remainder section
} while (TRUE);
(RMW) operation
● Semantics of the get_and_set() instruction: semantics
AtomicBoolean state = false;
boolean get_and_set(boolean target)
{ Atomic operation
boolean rv = state; – implemented by
state = target; hardware
return rv:
}
● Executed atomically
1. Returns the original value of the memory location on which the operation is
carried out
2. Shared Boolean variable lock, initialized to “FALSE” 🡺 lock is free
3. Set the new value of passed parameter to “TRUE” 🡺 try to lock
● release() {
state = false; // release lock
}
Spin Locks in a uni processor systems are considered bad pratice.
© Dr Yigal Hoffner 17 June 2020 22
Blocking implementation of acquire() and release()
● Another option is to use operating system facilities in order to block the process
asking to acquire the lock, in case the lock has already been acquired by another
process
● The process is blocked until the process which holds the lock releases the it by
doing a release()
● This requires the ability to block a process/thread when doing acquire() and wake
it up later when another process/thread executes the release() operation
● Locks which provide a combination of spinning and then suspending/blocking
after a certain interval are also available
● Return Value
● If successful, the pthread_mutex_lock() and pthread_mutex_unlock() functions
shall return zero; otherwise, an error number shall be returned to indicate the
error.
● The pthread_mutex_trylock() function shall return zero if a lock on the mutex
object referenced by mutex is acquired. Otherwise, an error number is returned to
indicate the error.
● There are different names for basically the same operations of Semaphores in
different languages:
● POSIX (C, C++)
4 Semaphore: sem_wait() sem_post()
4 Mutex: pthread_mutex_lock() pthread_mutex_unlock()
4 Spin Lock: pthread_spin_lock() pthread_spin_unlock()
● C#
4 Semaphore: waitOne() release()
● Java
4 Semaphore: acquire() release()
4 Java locks: lock() unlock()
(locks are similar in some ways to semaphores but also have some differences)
● signal() or up()
● Atomic action: Increment semaphore value by 1
S2;
/* Task P0 */ /* Task P1 */
S1; wait(sync); // Wait for
signal(sync); // Send the signal signal
S2;
© Dr Yigal Hoffner 17 June 2020 28
Semaphore implemented by OS freeze (blocking)
● A semaphore is a synchronization mechanism – implemented as an integer variable with 2 operations on it:
wait & signal
● The semaphore is an OS data structure and the wait & signal methods are part of the kernel
● Semaphores are used to control access to a common resource by multiple processes in a concurrent system
● A semaphore is a structure with 2 fields
● The two operations on a semaphore are (Dijkstra’s definition): wait() and signal()
● Must guarantee that no two processes can execute the wait()and signal() on same semaphore at the
same time – indivisibility of wait() and signal()
struct semaphore {
int value; // semaphore (counter) value;
struct process *queue // a queue of blocked processes
};
indivisible
indivisible
● Disadvantages of Semaphores
● Semaphores are complicated to use so the wait and signal operations must be
implemented in the correct order to prevent deadlocks
● Semaphores are impractical for large scale use as their use leads to loss of modularity.
This happens because the wait and signal operations prevent the creation of a structured
layout for the system
● Semaphores may lead to a priority inversion where low priority processes may access
the critical section first and high priority processes later
sem_t *sem = sem_open(“sema”, O_CREAT, 0644, 3); sem_t *sem = sem_open (“sema”, 0);
Producer Consumer
P0 P1
sem_open() sem_open()
Semaphore Q: 1
signal(struct
semaphore *s) { }
Kernel wait(struct
Data sempahore *s) { }
© Dr Yigal Hoffner 17 June 2020 35
Problems with Semaphores
wait(empty); wait(full);
buffer = data; data = buffer;
signal(full); signal(empty);
} }
Producer Consumer
do { // produce an item
do { // consumes item
wait(empty); wait(full);
signal(full); signal(empty);
} while (true)
} while (true)
wait(empty); wait(full);
wait(mutex); wait(mutex);
signal(mutex); signal(mutex);
signal(full); signal(empty);
// consumes item
} while (true) } while (true)
// eat
signal (chopstick[i] );
signal (chopstick[ (i + 1) % 5] );
// think
} while (TRUE);
● What is the problem with this algorithm?
● Deadlock handling
● Allow at most 4 philosophers to be sitting simultaneously at the table
● Allow a philosopher to pick up the forks only if both are available
(picking must be done in a critical section
● Use an asymmetric solution -- an odd-numbered philosopher picks up
first the left chopstick and then the right chopstick. Even-numbered
philosopher picks up first the right chopstick and then the left chopstick