Operating systems

Synchronization

Spring 2016 Francesco Fontanella Concurrent model

■ An OS executes many activities. Most of them are executed concurrently

■ It needs a suitable model which makes it possible the execution of these concurrent activities

■ This model is called concurrent model and it is based on the (abstract) concept of

Francesco Fontanella, Operating systems Spring 2016 2 Finite progress assumption

■ Concurrent model is based on the assumption (Finite progress assumption):

it is assumed that all the ready processes have positive rates of execution

NOTE no assumption is made about process execution rates

Francesco Fontanella, Operating systems Spring 2016 3 Concurrency

■ Two programs execute concurrently if they are executed in parallel ■ Two types of parallelism: – Real (more cores/CPUs) – Apparent (multiprogramming on a sigle CPU) ■ Two main problems: – Communication (IPC) – Synchronizzation

Francesco Fontanella, Operating systems Spring 2016 4 OS Concurrency

■ Multiprogramming – More processes run on the same processor: apparent parallelism ■ Multiprocessing – More processes on multiple processors: real parallelism ■ Distributed processing – More processes on different independent computers real parallelism

Francesco Fontanella, Operating systems Spring 2016 5 Multiprogramming vs Multiprocessing

■ Multiprogramming (single CPU): – More processes alternate on the same CPU (interleaved execution) – At a given instant, only one process is actually running

■ Multiprocessing (two ore more cores/CPUs) – More processes runs simultaneously on different CPUs/cores (overlapped execution)

Francesco Fontanella, Operating systems Spring 2016 7 Concurrent Programming: problems

■ Concurrent access to shared resources may cause data inconsistency

■ To preserve data consistency, we need a mechanism that guarantees the ordered access to the data shared by the processes

Francesco Fontanella, Operating systems Spring 2016 8 Concorruncy : example

C source code assembly void modify(int val) { .text amount = amount + val; modify: } lw $t0, amount % load add $t0, $t0, $a0 % add sw $t0, amount % store jr $ra % return ■ Suppose that: – A process P1 executes modify(+10) – A process P2 executes modify(­10) – P1 and P2 run concurrently – amount is a shared variable, initialized to 100 ■ Afterall, the value of the variable amount should be 100. Or not???

Francesco Fontanella, Operating systems Spring 2016 Scenario 1: multiprogramming (correct)

P1 lw $t0, amount amount=100, $t0=100, $a0=10 P1 add $t0, $t0, $a0 amount=100, $t0=110, $a0=10 P1 sw $t0, amount amount=110, $t0=110, $a0=10

OS interrupt OS P1 context stored OS P2 context restored amount=110, $t0=?, $a0=­10

P2 lw $t0, amount amount=110, $t0=110, $a0=­10 P2 add $t0, $t0, $a0 amount=110, $t0=100, $a0=­10 P2 sw $t0, amount amount=100, $t0=100, $a0=­10

Francesco Fontanella, Operating systems Spring 2016 Scenario 2: multiprogramming (error)

P1 lw $t0, amount amount=100, $t0=100, $a0=10

S.O. interrupt S.O. P1 context storing S.O. P2 context restoring amount=100, $t0=?, $a0=­10

P2 lw $t0, amount amount=100, $t0=100, $a0=­10 P2 add $t0, $t0, $a0 amount=100, $t0=90, $a0=­10 P2 sw $t0, amount amount= 90, $t0=90, $a0=­10

S.O. interrupt S.O. P2 context storing S.O. P1 context restoring amount=90, $t0=100, $a0=10

P1 add $t0, $t0, $a0 amount= 90, $t0=110, $a0=10 P2 sw $t0, amount amount=110, $t0=110, $a0=10 Francesco Fontanella, Operating systems Spring 2016 Scenario 3: multiprocessing (error)

■ The processes P1 and P2 are simultaneously executed on two different cores/CPUs

Process P1 Process P2 lw $t0, amount add $t0, $t0, $a0 lw $t0, amount sw $t0, amount add $t0, $t0, $a0 sw $t0, amount

NOTES – The processes work on different registers – The memory location of the variable must not be accessed simoultaneously Francesco Fontanella, Operating systems Spring 2016 Concurrency: remarks

■ No real difference between multiprogramming and multiprocessing ■ The OS provides the abstraction of a (virtual) processor for each process ■ Problems: – You cannot exactly predict when a given instruction is executed (finite progress assumption) – Processes access shared resources

Francesco Fontanella, Operating systems Spring 2016 Race condition

■ A set of concurrent processes have a race condition if the final result of their execution depends on the timing of the processes involved

■ Identifying and eliminating race conditions in the source code is crucial

Francesco Fontanella, Operating systems Spring 2016 ■ In concurrent programming, source code correctness also depends on the interaction among the various (possible) running instances of the code

■ Debugging concurrent applications is very hard: – Errors due to race conditions might occur very rarely

Francesco Fontanella, Operating systems Spring 2016 Producer/Consumer race condition

■ The two processes share the buffer and a counter variable

Producer Consumer while (true) { while (true) { while (count == BUFFER_SIZE) while (count == 0) ; // do nothing ; // do nothing buffer [in] = nextProduced; nextConsumed = buffer[out]; in = (in + 1) % BUFFER_SIZE; out = (out + 1) % BUFFER_SIZE; count++; count--; } /* consume the item in nextConsumed }

Francesco Fontanella, Operating systems Spring 2016 16 Source code with a race condition

do { in section

critical section

out section

remainder section (not critical) } while (true);

Francesco Fontanella, Operating systems Spring 2016 17 ■ Increment of the variable count (count++): register1 = count register1 = register1 + 1 count = register1

■ decrement of the variable count (count--): register2 = count register2 = register1 - 1 count = register2

Francesco Fontanella, Operating systems Spring 2016 18 Example

■ What does it happen if concurrently execute the instruction: count++ and cont-- (suppose count = 5):

S0: producer executes register1 = count {register1 = 5} S1: producer executes register1 = register1 + 1 {register1 = 6} S2: consumer executes register2 = count {register2 = 5} S3: consumer executes register2 = register2 - 1 {register2 = 4} S4: producer executes count = register1 {count = 6 } S5: consumer execurtes count = register2 {count = 4}

Francesco Fontanella, Operating systems Spring 2016 19 Program properties

■ In concurrent programming a program property is an attribute that must be true for every possible execution of the program

■ Two kind of properties: – Safety: the program does not make errors ("nothing bad happens")

– Liveness: the program always get the expected final state ("something good eventually happens")

Francesco Fontanella, Operating systems Spring 2016 Senquential program properties

■ In a sequential program: – Safety: correct final state (the result is as expected) – Liveness: termination

Francesco Fontanella, Operating systems Spring 2016 Concurrent programming: Safety

■ when access to shared resources, processes must not influence each other ■ Can be guaranteed through synchronization mechanisms

Francesco Fontanella, Operating systems Spring 2016 Concurrent programming: liveness

■ Synchronization mechanisms must not prevent program advancement

■ Must be avoided: – To suspend all processes waiting for an event that can be generated only from one of the suspended processes (deadlock) – No “indefinite waiting” for accessing to shared resources

Francesco Fontanella, Operating systems Spring 2016 (safety)

■ The access to a shared resource is mutually exclusive if, at every instant, no more than one process can access to the resource

■ example: – Two processes printing on the same printer – two processes communicate through a shared memor buffer

Francesco Fontanella, Operating systems Spring 2016 Deadlock (stall) (liveness)

■ Mutual exclusion may block indefinitely two processes ■ example:

– let P1 and P2 be two processes which try to

access concurrently to the resources R1 and R2

– Suppose that the OS assigns R1 to P1, and R2

to P2 – Both processes are indefinitely blocked (circular wait)

■ P1 and P2 are said deadlocked

Francesco Fontanella, Operating systems Spring 2016 Starvation (liveness)

■ It can also happens that a process cannot access to a resource because it is always “busy” (accessed by other processes) ■ Example – Let R be a resource

– let P1, P2, P3 be three processes which periodically access to R

– Suppose the P1 e P2 alternate using R

– P3 cannot access to R, because it is exclusively

used by P1 e P2

■ P3 is said to be in starvation

Francesco Fontanella, Operating systems Spring 2016 Atomic actions

■ Atomic actions are performed in an indivisible manner

■ Implementation depends on the type of parallelism: – Real: the atomic action must not interfere with those of other processes – Apparent: no during the action (interrupt disabled)

Francesco Fontanella, Operating systems Spring 2016 Examples

■ Single CPU instructions are atomic ■ Example: sw $a0, ($t0)

■ Apparent parallelism: – CPU hardware guarantees that interrupt are executed before or after an instruction ■ Real parallelism: – if more instructions (processors) try to access to the same memory cell, the memory bus arbiter is able to serialize the access

Francesco Fontanella, Operating systems Spring 2016 Atomic actions in C

■ In C, atomic actions depend on both compiler and processor ■ Examples – a=0; /* int a */ atomic: the a variable is defined as an integer and is initialized to 0 – a=0; /* long long a */ not atomic: the a variable is 64 bits long, this may require more instructions on a 32 bit cpu – a++; usually not atomic: depends on the CPU instruction set available

Francesco Fontanella, Operating systems Spring 2016 Solution requirements

■ Several solutions for the critical section problem

■ Solutions must satisfy three requirements: – Mutual exclusion – Progress – Bounded waiting

Francesco Fontanella, Operating systems Spring 2016 34 ■ Mutual solution: – if process is executing in its critical section, no other process is executing in its critical section ■ Progress – If a process P is not in the critical section, then P cannot prevent another process S from entering the critical section ■ Bounded waiting – If a process P is waiting on the critical section, then P will eventually enter the critical section

Francesco Fontanella, Operating systems Spring 2016 35 Possible approches

■ software – critical section accesses are directly managed by processes (their programmers) – Problems: • Possible errors! • busy waiting ■ hardware – Special hardware instructions – efficient – problems • Hardware dependent: low portability

Francesco Fontanella, Operating systems Spring 2016 ■ Other approaches are based on: – OS support – language support (e.g. Java)

■ Examples – Semaphores – Monitors – Message passing

Francesco Fontanella, Operating systems Spring 2016 Peterson's solution

■ Software approach ■ Two processes ■ Shared variables: int turn; boolean flag[2]; ■ Turn indicates which process can enter the critical section ■ The flag array indicates if a process is ready to enter its critical section

Francesco Fontanella, Operating systems Spring 2016 42 Critical section access

do { flag[i] = true; turn = j; while (flag[j] && turn == j) Busy waiting ;

critical section NOTES Satisfies the requirements of flag[i] = false; – mutual exclusion – progress – bounded waiting remainder section } while (true);

Francesco Fontanella, Operating systems Spring 2016 43 Hardware synchronization

■ Hardware solutions are available to solve the critical section problem ■ uniprocessor: – Interrupt disabling ■ multiprocessor – Interrupt disabling is time consuming – special atomic instructions: • They guarantee exclusive access to a given memory location ( variables)

Francesco Fontanella, Operating systems Spring 2016 45 Lock variables

■ A lock variable is a shared variable that is guaranteed to be accessed by a single process at any given time ■ Lock variables are used to implement mutual exclusion

remainder section

acquire lock

critical section

release lock

remainder section Francesco Fontanella, Operating systems Spring 2016 46 TESTandSET instruction

boolean TestAndSet (boolean *target) { boolean rv = *target; *target = true; return rv; }

Francesco Fontanella, Operating systems Spring 2016 47 TESTandSET hardware implementation

■ TESTandSET is implemented through a specifically devised hardware instruction: TSL register, lock ■ This instruction performs two actions: – Loads the content of the variable lock in register – Writes a not null value into the lock variable ■ Hardware guarantees the atomicity of the instruction ■ Atomicity is guaranteed by disabling the access to the memory bus to the other processors

Francesco Fontanella, Operating systems Spring 2016 48 Use of TESTandSET

remainder section while ( TestAndSet (&lock )) ; // do nothing

critical section

lock = false;

remainder section NOTE Does not satisfy the bounded waiting requirement

Francesco Fontanella, Operating systems Spring 2016 49 The SWAP instruction

void swap (boolean *a, boolean *b) { boolean temp = *a;

*a = *b; *b = temp: }

Francesco Fontanella, Operating systems Spring 2016 50 Hardware implementation of SWAP

■ The SWAP instruction is implemented through the following hardware instruction: XCHG register, lock ■ Swaps the content of the register with that of the lock variable ■ Atomicity is guaranteed by disabling the memory bus ■ To provide hardware synchronization, since x86 series, INTEL CPUs implements this instruction

Francesco Fontanella, Operating systems Spring 2016 51 Use of swap

remainder section key = true; while ( key == true) swap (&lock, &key );

critical section

lock = false;

remainder section

NOTE

Does not satisfy the bounded waiting requirement

Francesco Fontanella, Operating systems Spring 2016 52 Bounded waiting and mutual exclusion

do { waiting[i] = true; key = true; while (waiting[i] && key) key = TestAndSet(&lock); waiting[i] = false;

critical section

j = (i + 1) % n; while ((j != i) && !waiting[j]) j = (j + 1) % n; if (j == i) lock = false; else waiting[j] = false;

remainder section

} while (true); Francesco Fontanella, Operating systems Spring 2016 53 INTEL lock prefix

■ Nowadays the x86 instruction set allows all instructions to be atomic: ■ The lock prefix makes atomic any instruction following it Example lock sw $a0, ($t0) stores the value in the register $a0 at the memory address contained in the register $t0 ■ The lock prefix can be used with all the memory access instructions ■ This prefix has the effect of sending a lock to the memory bus

Francesco Fontanella, Operating systems Spring 2016 54 Semaphores

Francesco Fontanella, Operating systems Spring 2016 55 semaphores

■ Synchronization paradigm ■ Are implemented by means of signals: – Very short messages that allow process interaction ■ A process can be suspended at a given point until it receives a signal from another process

Francesco Fontanella, Operating systems Spring 2016 56 ■ A is an integer variable

■ Apart from initialization, a semaphore S can be accessed only through two standard atomic operations: – wait(S): performs two actions • Suspends the calling process if S<=0 • Decrements the value of S – signal(S) : increments the value of S

Francesco Fontanella, Operating systems Spring 2016 57 Semaphore definition

■ The code below defines the semantic of wait() and signal(): class Semaphore { private int val;

Semaphore(int init) { val = init; }

void wait() { while (val<=0) Atomicity can be guaranteed by ; // no op. the hardware instruction xchg val­­; }

void signal() { val++; } } Francesco Fontanella, Operating systems Spring 2016 58 Binary and counter semaphores

■ binary semaphore: only two values (0,1) – Can guarantee mutual exclusion (is called lock mutex)

■ counter semaphore: any value >= 0 – Can be used to share a resource with many units available

Francesco Fontanella, Operating systems Spring 2016 59 mutual exclusion

■ A binary semaphore can be used for mutual exclusion among N different processes

Semaphore mutex = 1; // initialized to 1 do { mutex.wait();

critical section

mutex.signal();

remainder section } while (true);

Francesco Fontanella, Operating systems Spring 2016 60 Resource sharing

If x is 1 you get mutual exclusion Semaphore s=x; If x > 1 you get a controlled do { access to resource with x units s.wait(); available

critical Section

s.signal(); // remainder section } while (true);

Francesco Fontanella, Operating systems Spring 2016 61 Instruction synchronizzation

■ Scenario – Two processes P1 and P2 execute the instructions S1 and S2, respectively – S1 must be executed before S2 ■ solution

P1 P2

S1; sync.wait(); sync.signal(); S2;

QUESTION What is the initial value of the semaphore sync ?

Francesco Fontanella, Operating systems Spring 2016 62 Semaphore implementation

■ Previous semaphore definition uses busy waiting:

wait (S) { while S <= 0 ; // no­op S­­; }

Francesco Fontanella, Operating systems Spring 2016 63 Busy waiting solution ■ if the semaphore S is <= 0, the wait() function suspends the calling process P: – P is moved from the ready process queue to the waiting queue of the semaphore S ■ The process P is resumed after a signal() call from another process ■ To this aim the class Semaphore must be modified, adding a list for the processes suspended on the semaphore: – class Semaphore { int val; struct task_struct *list;

It can be a FIFO } semaphore; Francesco Fontanella, Operating systems Spring 2016 64 New Semaphore

class Semaphore { private int val;

private struct task_struct *p_list;

Semaphore(int init) { val = init; } void wait(); void signal(); }

Francesco Fontanella, Operating systems Spring 2016 65 Semaphore::wait() { val­­; if (val < 0) { add me to S­>p_list; Remove the calling process form the block(); ready process queue } }

Semaphore::signal() { val++; NOTE if (val <= 0) { block() are remove the process P from S­>list; wakeup() syscalls wakeup(P); } }

Moves the caller process back to the queue of the ready processes

Francesco Fontanella, Operating systems Spring 2016 66 ■ It must be guaranteed that signal() and wait() are not concurrently executed (on the same semaphore).

■ Implementation issues: – Semaphores with wait list are suitable only when very long critical sections (> 1 second) are expected – Otherwise busy waiting does not create any problem

Francesco Fontanella, Operating systems Spring 2016 67 Deadlock

■ Suppose you have two semaphores, shared by the processes

P0 e P1 :

P0 P1 wait(S) wait(Q) wait(Q) wait(S) ...... signal(S) signal(Q) signal(Q) signal(S)

Francesco Fontanella, Operating systems Spring 2016 69 Priority inversion

■ High priority processes share resources with lower priority processes

■ Example: – Scenario: 3 processes L, M and H, with priorities L

Francesco Fontanella, Operating systems Spring 2016 71 ■ Solution: – Priority inheritance: while accessing the resource R, the process L inherits the priority of H ■ The priority inversion problem affected even the Mars pathfinder robot, which hosted the VxWorks OS. ■ A full description of the problem is available here:

http://research.microsoft.com/.../authoritative_account.html

Francesco Fontanella, Operating systems Spring 2016 72 Mutual exclusion: example

■ Scenario: a parking area has a capacity of MAX seats ■ When a new car arrives: – It a seat is avaialble, it enters, stays for while and then exits – If the parking is full it goes away ■ PROGRAM ■ Write a C program that, using the pthread library, controls theparking access ■ A car is represented by a single . While the thread is represented by a shared variable ■ If a seat is available, the thread update the state of the parking (the shared variable), otherwise it terminates ■ Once in the parking area, cars (threads) stays inside for a random time, and then go away (thread terminates).

Francesco Fontanella, Operating systems Spring 2016 73 Mutual exclusion in Pthread

■ pthread allows mutual exclusion (MUTual EXclusion, MUTEX) ■ Pthread defines the type pthread_mutex_t. Which is a binary semaphore, with two possible states: – Locked – Unlocked ■ Only one thread at a time can block (lock) a mutex: – If another thread attempts to block the mutex, it is suspended until the mutex is unblocked (unlocked)

Francesco Fontanella, Operating systems Spring 2016 75 Pthread mutex: functions

■ int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *m_attr) – Initializes the mutex pointed by the parameter m, using the attributes specified by m_attr. – If am_attr is NULL then the default values are used

■ int pthread_mutex_lock(pthread_mutex_t *m) – Blocks (attempt to) the mutex pointed by the parameter m – If m has been previously blocked by another thread, the calling thread is suspended

Francesco Fontanella, Operating systems Spring 2016 76 ■ int pthread_mutex_trylock(pthread_mutex_t *mutex) – As pthread_mutex_lock except that if the mutex is already locked, the calling thread is not suspended

■ int pthread_mutex_unlock(pthread_mutex_t *m) – Unlocks the mutex pointed by m – It is assumed that the m has been previously locked by the caller

Francesco Fontanella, Operating systems Spring 2016 77 Pthread mutex: problems

■ What happens if: pthread_mutex_lock(&m) . . . pthread_mutex_lock(&m)

What happens if a thread calls pthread_mutex_unlock(&m) and m has been previously locked by another thread?

Francesco Fontanella, Operating systems Spring 2016 78 Mutex types

■ normal: – No checks are performed – Errors like those seen before cause problems

■ Error checking: – Programming errors are managed returning error codes to the calling process

Francesco Fontanella, Operating systems Spring 2016 79 Car thread pthread_mutex_t M; int in=0; void *thread_car(void *arg) /*car thread source code */ { int id, t; id=*((int *)arg); srand((int)pthread_self()); Critical section

pthread_mutex_lock(&M); if (in < CAPACITY) // entry { in++; printf("car #%d parked – there are %d in the parking\n", id, in); pthread_mutex_unlock(&M); else { // no seats available: terminates pthread_mutex_unlock(&M); return 0; } t = rand() % MAX_TIME; // parking duration (t); to be continued... Francesco Fontanella, Operating systems Spring 2016 80 Critical section pthread_mutex_lock(&M); in--; printf("car #%d exit – there are %d cars in the parking\n", id, in); pthread_mutex_unlock(&M);

return t; }

Francesco Fontanella, Operating systems Spring 2016 81 Main main (int argc, char *argv[]) { pthread_t T[MAX]; pthread_mutex_init (&M, NULL); int i;

for (i=0; i

for (i=0; i < MAX; i++) { pthread_join(T[i], (void *)&A[i]); if (A[i]> 0) printf("car %d was parked for %d seconds\n", i, A[i]); else printf("car %d did not entry \n", i); }

return 0; }

Francesco Fontanella, Operating systems Spring 2016 82 Semaphores: examples

■ Resource pool management

■ Producer/consumer

■ Reader/writer

■ Five philosophers

Francesco Fontanella, Operating systems Spring 2016 83 Resource pool management

■ Any free resource of the pool can be used ■ It needs a manager for storing resource states ■ Resources are allocated/deallocated by the manager ■ Once acquired, a resource can be used without worrying about mutual exclusion ■ Once finished, the resource must be returned to the manager

Francesco Fontanella, Operating systems Spring 2016 84 Solution 1

#define NUM_RES 10

struct struct_res { semaphore mutex = 1; /* mutex */ int res_num = N; /* #resources available . */ boolean free[N]; /*free resources flags*/ };

void init(struct_res &r) { int i; /*iniziatialization*/ for(i=0; i < NUM_RES; i++) r.free[i]=true; }

Francesco Fontanella, Operating systems Spring 2016 85 int request(struct_res &r) { int i=0;

wait(r.mutex); while(r.res_num == 0) { Busy waiting signal(r.mutex); wait(r.mutex); } r.res_num­­; QUESTION while(!r.free[i++]) Does it work? ; If yes, why? r.free[i]=false; signal(r.mutex); void release(struct_res &r, int i) return i; { } wait(r.mutex); r.free[i] = true; r.res_num++; signal(r.mutex); } Francesco Fontanella, Operating systems Spring 2016 86 Solution 2

#define NUM_RES 10

struct struct_res { semaphore mutex = 1; /*mutex*/ semaphore res = NUM_RES; /* resource semaphore */ boolean free[N]; /*free resources flags*/ };

void init(struct_res) { int i; /*iniziatialization*/ for(i=0; i < NUM_RES; i++) free[i]=true; }

Francesco Fontanella, Operating systems Spring 2016 87 int request(struct_res &r) { int i=0; wait(r.res); NO busy waiting wait(r.mutex);

while(!r.free[i++]) ; r.free[i]=false; signal(r.mutex);

return i; } void release(struct_res &r, int i) { wait(r.mutex); r.free[i] = true; signal(r.mutex); signal(r.res); }

Francesco Fontanella, Operating systems Spring 2016 88 Producer/Consumer

■ Two processes: – producer: inserts items in a buffer (circular) – Consumer: extracts items

Circular buffer

out in

in out

Francesco Fontanella, Operating systems Spring 2016 89 void producer(item buffer[], int &in, int out)

item tmp; QUESTION while (true) { Which are the /* item production */ critical sections? . . /* waits if the buffer is full */ while (( (in + 1) % BUFFER_SIZE ) == out) ; buffer[in] = item;

in = (in + 1) % BUFFER SIZE; } }

Francesco Fontanella, Operating systems Spring 2016 90 void consumer(item buffer[], int in, int &out) { item tmp QUESTION while (true) { Which are the while (in == out) critical sections? ; // empty queue: waiting

// item extraction tmp = buffer[out];

out = (out + 1) % BUFFER SIZE;

/* the item is used */ . . } }

Francesco Fontanella, Operating systems Spring 2016 91 void consumer(item buffer[], int in, int &out) { item tmp QUESTION while (true) { Which are the while (in == out) critical sections? ; // empty queue: waiting

// item extraction tmp = buffer[out];

out = (out + 1) % BUFFER SIZE;

/* the item is used */ . . } }

Francesco Fontanella, Operating systems Spring 2016 92 Producer/Consumer: semaphores

■ How can we use a mutex semaphore?

■ What does the semaphore must guarantee?

■ Which variables must be accessed in mutex ?

Francesco Fontanella, Operating systems Spring 2016 93 Binary semaphore mutex; semaphore void producer(item buffer[], int &in, int out) {

item tmp;

while (true) { /* item production*/ . . /* waits if the buffer is full */ wait(mutex); while (( (in + 1) % BUFFER_SIZE ) == out){ signal(mutex); wait(mutex); }

buffer[in] = item;

wait(mutex); in = (in + 1) % BUFFER SIZE; signal(mutex); } }

Francesco Fontanella, Operating systems Spring 2016 94 semaphore mutex; void consumer(item buffer[], int in, int &out) { item tmp

while (true) {

wait(mutex); while (in == out) // empty queue: waiting signal(mutex); wait(mutex);

// item extraction tmp = buffer[out];

wait(mutex); out = (out + 1) % BUFFER SIZE; signal(mutex);

/* the item is used*/ . . } }

Francesco Fontanella, Operating systems Spring 2016 95 ■ Previous solution – busy waiting

■ Can we avoid busy waiting, using semaphores?

■ What are the busy waiting conditions: – Buffer empty – Buffer full

Francesco Fontanella, Operating systems Spring 2016 96 ■ Three semaphores: – empty (initialized to BUFFER_SIZE) – full (initialized a 0) – mutex (initialized a 1)

producer Consumer while (true) { while (true) { wait(empty); wait(full);

// add an item // remove an item . . . . wait(mutex); wait(mutex) count++; count­­; signal(mutex) signal(mutex) signal(full) signal(empty) } }

Francesco Fontanella, Operating systems Spring 2016 97 Readers/writers problem

■ Description – A database is shared among many processes – Two types of processes: • Readers: read from the database • writers: write to the database (update) ■ requirements – Writers must access in mutual exclusion: no other writer or reader can concurrently access the database – multiple readers can concurrently access the database

Francesco Fontanella, Operating systems Spring 2016 98 Solutions

■ Which data structures? ■ How many variables/semaphores? ■ What about the writer process? What it must take into account? ■ What about the reader process?

Francesco Fontanella, Operating systems Spring 2016 99 Readers/writers with semaphores

Data structures: semaphore mutex = 1, wrt = 1; int readcount = 0; reader

do { wait(mutex); writer readcount++; if (readcount == 1) do { wait(wrt); wait(wrt); signal(mutex)

// writing... // reading ... wait (mutex); signal(wrt); readcount­­; } while (true) if (readcount == 0) signal(wrt); signal(mutex); } while (true);

Francesco Fontanella, Operating systems Spring 2016 100 Problems

■ Previous solution: – Possible starvation both for readers and writers

■ How to solve the problem?

■ How the source code should be modified? where?

■ What must be taken into account?

Francesco Fontanella, Operating systems Spring 2016 101 Variables

writer Semaphore mutex=1; Semaphore readers=0, do { write_request(); Semaphore writers=0; int read_susp=0; // writing... int wrt_susp=0; int read_in =0; write_end(); bool wrt_in=false; } while (true)

reader do { read_request();

// writing...

read_end(); } while (true)

Francesco Fontanella, Operating systems Spring 2016 102 void write_request() void read_end() { { wait(mutex); wait(mutex); if (read_in || read_in­­; wrt_in) { if (read_in == 0 && wrt_susp++; wrt_in) signal(mutex); signal(writers); wait(writers); else signal(mutex); wrt_susp­­; } }

wrt_in = true; signal(mutex);

}

Francesco Fontanella, Operating systems Spring 2016 103 void read_request() void write_end() { { wait(mutex); wait(mutex); if (wrt_susp > 0 || wrt_in = false; wrt_in) { if (read_susp) read_susp++; signal(readers); signal(mutex); else signal(mutex); wait(readers); } read_susp--; }

read_in++; if (read_susp > 0) signal(readers); all readers are woken up, one by one else signal(mutex);

}

Francesco Fontanella, Operating systems Spring 2016 104 Dining philosophers problem

■ Five philophers spend their lives thinking and eating ■ They sit at a round table ■ At center of the table there is a bowl of rice ■ Each philosopher has a chopstick on its right ■ To eat a philosopher needs both left and right chopstick ■ A philosopher may pick up only a chopstick at a time

Francesco Fontanella, Operating systems Spring 2016 105 NOTE The problem was originally formulated in 1965 by Edsger Dijkstra as a student exam exercise, presented in terms of computers competing for access to tape drive peripherals Francesco Fontanella, Operating systems Spring 2016 106 i-th philosopher source code do { wait(chopstick[i]); wait(chopstick[(i+1)%5]);

// eat

signal(chopstick[i]); signal(chopstick[(i+1)%5]);

// think

} while (true); NOTE Each chopstick is represented by a semaphore (initialized to 1)

Francesco Fontanella, Operating systems Spring 2016 107 Correct solution (no deadlock) process philo[0] { process philo[i] { /* i = 1...4 */

while (true) { while (true) { think think chopstick[1].wait(); chopstick[i].wait(); chopstick[0].wait(); chopstick[(i+1)%5].wait(); eat eat chopstick[1].signal(); chopstick[i].wait(); chopstick[0].signal(); chopstick[(i+1)%5].wait(); } } } }

Francesco Fontanella, Operating systems Spring 2016 108 Other solutions

■ Other possible solutions – even index philosphers are left-handed, and the others are right-handed – in case of collision, a philosopher must wait that its neighbors terrminate eating – At most four philophers can eat concurrently (counter variable) – Two chopsticks must be taken simultaneosly (a semaphore for each couple of chopsticks)

■ What about starvation?

Francesco Fontanella, Operating systems Spring 2016 109 Linux kernel synchronizaction

■ Synchronization is a key aspect for any OS ■ UNIX kernels are reentrant: – More processes can be executed in kernel mode concurrently ■ This feature needs locking mechanims ■ It must be avoided that more kernel mode processes concurrently access shared data structures Example Linux queue load balancing

Francesco Fontanella, Operating systems Spring 2016 110 Linux kernel synchronization mechanisms

■ kernel preemption disabling ■ interrupt disabling ■ atomic operations ■ spin locks ■ Semaphores

Francesco Fontanella, Operating systems Spring 2016 111 kernel preemption disabling

■ The Linux kernel is preemptable: – A process executing in kernel mode can be suspended by another process ■ It is effective on uniprocessor systems: there is no concurrent access to the kernel shared data structures

Disable kernel preemption

Critical section

Enable kernel preemption

NOTE Processes must check data consistency Francesco Fontanella, Operating systems Spring 2016 112 Interrupt disabling

■ Only on uniprocessor systems

■ Long critical sections cause a strong reduction of the use of peripheral thoughtput (disks, keyboard, mouse, etc.)

Francesco Fontanella, Operating systems Spring 2016 113 Atomic operations

■ Many assembly instructions are read-modify-write, and make two memory accesses: – The first for reading the current value of a memory cell – The second to write a new value ■ Many OS provide hardware-based atomic instructions – x86 assembly LOCK prefix makes atomic the instruction it preceeds ■ Example of use – Shared counter (producer/ consumer problem) ■ The kernel type atomic_t allows the implementation of atomic operations

Francesco Fontanella, Operating systems Spring 2016 114 Spin locks

■ busy wait semaphores: – If the lock is blocked then it executes an empty cycle (it spins) ■ Useful in multi-processor systems ■ Very useful for kernel tasks: – Many resources are locked for a very short time (even fractions of ms) – In this case, process suspension is much more costly! ■ Example wait queue

struct wait_queue_head { spinlock_t lock; struct list_head task_list; }

Francesco Fontanella, Operating systems Spring 2016 115 Read/write spin locks

■ Spin lock optimization, improves kernel concurrency ■ distinguishes between read and write operations ■ Allows multiple/single reading/writing accesses ■ Writings are allowed only if there are no reading in progress

Francesco Fontanella, Operating systems Spring 2016 116 Semaphores

■ For multi-processor systems ■ Differently from spin locks, processes requiring already blocked semaphore are suspended ■ Cannot be used by processes that cannot be suspended (e.g. interrupt handlers) ■ Also a read/write version

Francesco Fontanella, Operating systems Spring 2016 117 Monitors

Francesco Fontanella, Operating systems Spring 2016 118 Semaphores: Problems

signal(mutex); wait(mutex);

// critical section // critical section

wait(mutex) wait(mutex)

NO MUTUAL EXCLUSION! STARVATION

■ What happens if you omit wait(), signal() or both?

NOTE Remember that identyfing these errors is really hard: reproducing the conditions that cause these errors is not an easy task!!

Francesco Fontanella, Operating systems Spring 2016 119 Monitors

■ a monitor is a software module made of: – local data (information hiding) – an initialization step – one or more procedures ■ A process enters the monitor by invoking one of its procedure ■ A process at a time can be active inside the monitor (mutual exclusion)

Francesco Fontanella, Operating systems Spring 2016 120 Francesco Fontanella, Operating systems Spring 2016 121 Monitor - Sintax

monitor name {

variable declarations... Monitor private variables

type procedurename1(args...) {

... Public procedures

}

type procedurename2(args...) {

... Private procedures

}

name(args...) {

... iniziatialization

} } Francesco Fontanella, Operating systems Spring 2016 Monitor – condition variables

■ Condition variable (CV) ha – condition c; ■ CV operations: – c.wait() waits for the occurence of the condition represented by c (the process is suspended) – c.signal() signals that the condition is true

Francesco Fontanella, Operating systems Spring 2016 Signal urgent policy

■ c.wait() – Releases the mutex – The calling process is suspended on waiting queue for the condition represenetd by the CV c

■ c.signal() – wakes up a process queued on c (FIFO policy) – The calling process is suspended and it will be reactivated once the awaken process releases the mutex – If no process is waiting for the condition c, the call has no effect

Francesco Fontanella, Operating systems Spring 2016 Urgent queue

condition variable input queue Monitor

enqueue: c.wait() dequeue: c.signal()

Francesco Fontanella, Operating systems Spring 2016 Signaling policies

■ It exists also other policies: – SW - signal wait • no urgent stack, the process which call the signal procedure is inserted in the entry queue – SR - signal and return • After the signal the process exits the monitor – SC - signal and continue • The signal warns that a process waiting for c can continue, but it continues to execute • When the caller leaves the monitor, a process queued on c is woken up

Francesco Fontanella, Operating systems Spring 2016 Signal all

■ It is also possible to wake up all the waiting processes on c, by using the procedure signal_all

■ It is a variant of the signal-and-continue policy

■ All the woken up processes are added to the entry queue

Francesco Fontanella, Operating systems Spring 2016 Condition variables vs semaphores

■ What are the differences between semaphores and condition variables (CV)?

■ Wait: – CV: it always blocks the caller – Semaphores: NO (if semaphore > 0)

■ Signal: – CV: it has no effect if there are no processes queued on c – Semaphores: "store" every event

Francesco Fontanella, Operating systems Spring 2016 129 Eating philosophers with monitors

monitor DP { enum { THINKING, HUNGRY, EATING} state[5] ; condition self[5]; void pickup (int i) { state[i] = HUNGRY; test(i); if (state[i] != EATING) self[i].wait(); } void putdown (int i) { state[i] = THINKING; // test left and right neighbors test((i + 4) % 5); test((i + 1) % 5); }

Francesco Fontanella, Operating systems Spring 2016 130 void test (int i) { if ((state[(i+4)%5] != EATING) && (state[i] == HUNGRY) && (state[(i+1)%5] != EATING) ) { state[i] = EATING ; self[i].signal() ; } } void init() { int i; for (i = 0; i < 5; i++) state[i] = THINKING; } }

Francesco Fontanella, Operating systems Spring 2016 131 L'i-th philosopher

DP DiningPhilosophers;

. . .

DiningPhilosophers.pickup(i);

// EAT

DiningPhilosophers.putdown(i);

Francesco Fontanella, Operating systems Spring 2016 132 Producer/Consumer with monitor

monitor Prod_Cons { // variables Object[] buffer; condition okRead, okWrite; int count, in, out, size;

// procedures Prod_Cons(int size); Object read(); void write(Object val); }

Francesco Fontanella, Operating systems Spring 2016 Consumer

Object Prod_Cons::read() { Object retval;

if (count == 0) okRead.wait(); retval = buffer[rear]; cont­­;

in = (in+1) % buffer.length; okWrite.signal();

return retval; } Francesco Fontanella, Operating systems Spring 2016 Producer

Prod_Cons::write(Object val) { if (count == buffer.length) okWrite.wait();

buffer[out] = val; count++;

out = (out+1) % buffer.length; okRead.signal(); } }

Francesco Fontanella, Operating systems Spring 2016 Monitor implementation by semaphores: urgent wait

■ A binary semaphore can be used to ensure mutual exclusion inside the monitor: – semaphore mutex; // (initially = 1)

■ Another semaphore is needed to suspend the processes that make a signal call to condition variables inside the monitor: – semaphore urgent; // (initially = 0) – int u­count = 0;

Francesco Fontanella, Operating systems Spring 2016 136 Monitor procedures

type Monitor_name::procedurename(args...) { wait(mutex); … // body of F; …

if (u­count > 0) Checks if there are processes (inside the monitor) suspended on the semaphor urgent signal(urgent) If so, they must be woken up, otherwise the mutex else signal(mutex); for accessing the monitor is released ...

}

Francesco Fontanella, Operating systems Spring 2016 137 Condition Variables with semaphores

■ For every condition variable are needed two variables: struct Condition { semaphore sem; // (initially = 0) int count = 0; }

Francesco Fontanella, Operating systems Spring 2016 138 urgent wait semaphores: condition variables

void wait(Condition cond) void signal(condition cond) { cond.count++; if (cond.count > 0) { if (u­count > 0) u­count++; signal(urgent); signal(cond.sem); else signal(mutex); wait(urgent);

u­count­­; wait(cond.sem);

cond.count­­; return; } return; } Francesco Fontanella, Operating systems Spring 2016 139 Monitor implementation by semaphores: signal and continue

type Monitor_name::procedurename(args...) { wait(mutex); … // body of F; …

signal(mutex);

}

Francesco Fontanella, Operating systems Spring 2016 140 void wait(Condition cond) { void signal(condition cond) cond.count++; { signal(mutex); if (cond.count > 0) { wait(cond.sem); cond.count­­; wait(mutex); signal(cond.sem); } return; } return; }

Francesco Fontanella, Operating systems Spring 2016 141 Monitor implementation by semaphores: signal and wait

type Monitor_name::procedurename(args...) { wait(mutex); … // body of F; …

signal(mutex);

}

Francesco Fontanella, Operating systems Spring 2016 142 void wait(condition cond) { void signal(condition cond) cond.count++; { signal(mutex); if (cond.count > 0) { wait(cond.sem); cond.count­­; signal(cond.sem); return; wait(mutex); } } return; }

Francesco Fontanella, Operating systems Spring 2016 143 Monitor implementation by semaphores: signal and return

type Monitor_name::procedurename(args...) { wait(mutex); … // body of F; …

If the procedure does not contain any signal the it ends with a signal(mutex) otherwise it ends with a signal(x) }

Francesco Fontanella, Operating systems Spring 2016 144 void wait(condition cond) { void signal(condition cond) cond.count++; { signal(mutex); if (cond.count > 0) wait(cond.sem); signal(cond.sem); cond.count­­; else signal(mutex);

return; } return; }

Francesco Fontanella, Operating systems Spring 2016 145