0% found this document useful (0 votes)
22 views99 pages

05 Sync Part1

pioko

Uploaded by

epinipi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
22 views99 pages

05 Sync Part1

pioko

Uploaded by

epinipi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 99

Processes Synchronization - Part I

Amir H. Payberah
[email protected]
Nov. 14, 2023
Processes Synchronization

1 / 46
Background

I Processes can execute concurrently.

2 / 46
Background

I Processes can execute concurrently.

https://tinyurl.com/2yjcpx75

2 / 46
Background

I Processes can execute concurrently.


I Concurrent access to shared data may result in data inconsistency.

https://tinyurl.com/2yjcpx75

2 / 46
Background

I Processes can execute concurrently.


I Concurrent access to shared data may result in data inconsistency.
I Maintaining data consistency requires mechanisms to ensure the orderly
execution of cooperating processes.

https://tinyurl.com/2yjcpx75

2 / 46
Producer-Consumer Problem

I The producer-consumer problem.

3 / 46
Producer-Consumer Problem

I The producer-consumer problem.


I Having an integer counter that keeps track of the number of items in the
buffers.

3 / 46
Producer-Consumer Problem

I The producer-consumer problem.


I Having an integer counter that keeps track of the number of items in the
buffers.
• Initially, counter is set to 0.

3 / 46
Producer-Consumer Problem

I The producer-consumer problem.


I Having an integer counter that keeps track of the number of items in the
buffers.
• Initially, counter is set to 0.
• The producer produces a new item: increment the counter

3 / 46
Producer-Consumer Problem

I The producer-consumer problem.


I Having an integer counter that keeps track of the number of items in the
buffers.
• Initially, counter is set to 0.
• The producer produces a new item: increment the counter
• The consumer consumes an item: decrement the counter

3 / 46
Producer

I Producer

while (true) {
/* produce an item in next produced */

while (counter == BUFFER_SIZE); /* do nothing */

buffer[in] = next_produced;

in = (in + 1) % BUFFER_SIZE;

counter++;
}

4 / 46
Consumer

I Consumer

while (true) {
while (counter == 0); /* do nothing */

next_consumed = buffer[out];

out = (out + 1) % BUFFER_SIZE;

counter--;
/* consume the item in next consumed */
}

5 / 46
Race Condition

I counter++ could be implemented as


register1 = counter
register1 = register1 + 1
counter = register1

6 / 46
Race Condition

I counter++ could be implemented as


register1 = counter
register1 = register1 + 1
counter = register1
I counter-- could be implemented as
register2 = counter
register2 = register2 - 1
counter = register2

6 / 46
Race Condition

I counter++ could be implemented as


register1 = counter
register1 = register1 + 1
counter = register1
I counter-- could be implemented as
register2 = counter
register2 = register2 - 1
counter = register2
I Consider this execution interleaving with count = 5 initially:

6 / 46
Race Condition

I counter++ could be implemented as


register1 = counter
register1 = register1 + 1
counter = register1
I counter-- could be implemented as
register2 = counter
register2 = register2 - 1
counter = register2
I Consider this execution interleaving with count = 5 initially:
S0: producer: register1 = counter: register1 = 5

6 / 46
Race Condition

I counter++ could be implemented as


register1 = counter
register1 = register1 + 1
counter = register1
I counter-- could be implemented as
register2 = counter
register2 = register2 - 1
counter = register2
I Consider this execution interleaving with count = 5 initially:
S0: producer: register1 = counter: register1 = 5
S1: producer: register1 = register1 + 1: register1 = 6

6 / 46
Race Condition

I counter++ could be implemented as


register1 = counter
register1 = register1 + 1
counter = register1
I counter-- could be implemented as
register2 = counter
register2 = register2 - 1
counter = register2
I Consider this execution interleaving with count = 5 initially:
S0: producer: register1 = counter: register1 = 5
S1: producer: register1 = register1 + 1: register1 = 6
S2: consumer: register2 = counter: register2 = 5

6 / 46
Race Condition

I counter++ could be implemented as


register1 = counter
register1 = register1 + 1
counter = register1
I counter-- could be implemented as
register2 = counter
register2 = register2 - 1
counter = register2
I Consider this execution interleaving with count = 5 initially:
S0: producer: register1 = counter: register1 = 5
S1: producer: register1 = register1 + 1: register1 = 6
S2: consumer: register2 = counter: register2 = 5
S3: consumer: register2 = register2 - 1: register2 = 4

6 / 46
Race Condition

I counter++ could be implemented as


register1 = counter
register1 = register1 + 1
counter = register1
I counter-- could be implemented as
register2 = counter
register2 = register2 - 1
counter = register2
I Consider this execution interleaving with count = 5 initially:
S0: producer: register1 = counter: register1 = 5
S1: producer: register1 = register1 + 1: register1 = 6
S2: consumer: register2 = counter: register2 = 5
S3: consumer: register2 = register2 - 1: register2 = 4
S4: producer: counter = register1: counter = 6

6 / 46
Race Condition

I counter++ could be implemented as


register1 = counter
register1 = register1 + 1
counter = register1
I counter-- could be implemented as
register2 = counter
register2 = register2 - 1
counter = register2
I Consider this execution interleaving with count = 5 initially:
S0: producer: register1 = counter: register1 = 5
S1: producer: register1 = register1 + 1: register1 = 6
S2: consumer: register2 = counter: register2 = 5
S3: consumer: register2 = register2 - 1: register2 = 4
S4: producer: counter = register1: counter = 6
S5: consumer: counter = register2: counter = 4

6 / 46
What’s The Output?

int counter = 0;

void* thread_func(void *arg) {


counter++;
printf("Job %d started.\n", counter);
sleep(2);
printf("Job %d finished.\n", counter);

return NULL;
}

int main(void) {
pthread_t t1, t2;

pthread_create(&t1, NULL, &thread_func, NULL);


pthread_create(&t2, NULL, &thread_func, NULL);

pthread_join(t1, NULL);
pthread_join(t2, NULL);

return 0;
}

7 / 46
What’s The Output?

Job 1 started.
Job 2 started.
Job 2 finished.
Job 2 finished.

8 / 46
The Critical-Section (CS)
Problem

9 / 46
The Critical-Section Problem (1/2)

I Consider system of n processes {p0 , p1 , · · · , pn−1 }.

10 / 46
The Critical-Section Problem (1/2)

I Consider system of n processes {p0 , p1 , · · · , pn−1 }.

I Each process has CS segment of code.

10 / 46
The Critical-Section Problem (1/2)

I Consider system of n processes {p0 , p1 , · · · , pn−1 }.

I Each process has CS segment of code.


• Process may be changing common variables, updating table, writing file, etc.

10 / 46
The Critical-Section Problem (1/2)

I Consider system of n processes {p0 , p1 , · · · , pn−1 }.

I Each process has CS segment of code.


• Process may be changing common variables, updating table, writing file, etc.
• When one process in CS, no other may be in its CS.

10 / 46
The Critical-Section Problem (2/2)

I Each process must ask permission to enter CS in entry section, may follow
CS with exit section, then remainder section.

11 / 46
The Critical-Section Problem (2/2)

I Each process must ask permission to enter CS in entry section, may follow
CS with exit section, then remainder section.
I General structure of process Pi is below:

11 / 46
CS Problem Solution Requirements (1/3)

I Mutual Exclusion: if process Pi is executing in its CS, then no other


processes can be executing in their CSs.

12 / 46
CS Problem Solution Requirements (2/3)

I Progress: if no process is executing in its CS and there exist some pro-


cesses that wish to enter their CS, then the selection of the processes
that will enter the CS next cannot be postponed indefinitely.

13 / 46
CS Problem Solution Requirements (3/3)

I Bounded Waiting: a bound must exist on the number of times that


other processes are allowed to enter their CSs after a process has made
a request to enter its CS and before that request is granted.

14 / 46
CS Solutions

I Peterson’s solution

I Mutex lock

I Semaphore

15 / 46
Peterson’s Solution

16 / 46
Peterson’s Solution

I Two-process solution.

17 / 46
Peterson’s Solution

I Two-process solution.
I The two processes share two variables:
• int turn
• boolean flag[2]

17 / 46
Peterson’s Solution

I Two-process solution.
I The two processes share two variables:
• int turn
• boolean flag[2]

I turn: indicates whose turn it is to enter the CS.

17 / 46
Peterson’s Solution

I Two-process solution.
I The two processes share two variables:
• int turn
• boolean flag[2]

I turn: indicates whose turn it is to enter the CS.

I flag: indicates if a process is ready to enter the CS, i.e.,


flag[i] = true implies that process Pi is ready.

17 / 46
Algorithm for Process Pi

18 / 46
CS Requirements

I Provable that the three CS requirement are met:

19 / 46
CS Requirements

I Provable that the three CS requirement are met:


1. Mutual exclusion is preserved:
Pi enters CS only if: either flag[j] = false or turn = i

19 / 46
CS Requirements

I Provable that the three CS requirement are met:


1. Mutual exclusion is preserved:
Pi enters CS only if: either flag[j] = false or turn = i
2. Progress requirement is satisfied.

19 / 46
CS Requirements

I Provable that the three CS requirement are met:


1. Mutual exclusion is preserved:
Pi enters CS only if: either flag[j] = false or turn = i
2. Progress requirement is satisfied.
3. Bounded-waiting requirement is met.

19 / 46
Mutex Locks

20 / 46
Mutex Locks

I Protect a CS by first acquire() a lock then release() the lock.


• Boolean variable indicating if lock is available or not.

21 / 46
Mutex Locks

I Protect a CS by first acquire() a lock then release() the lock.


• Boolean variable indicating if lock is available or not.

I Calls to acquire() and release() must be atomic.


• Usually implemented via hardware atomic instructions.

21 / 46
Mutex Locks

I Protect a CS by first acquire() a lock then release() the lock.


• Boolean variable indicating if lock is available or not.

I Calls to acquire() and release() must be atomic.


• Usually implemented via hardware atomic instructions.

I But this solution requires busy waiting.


• This lock therefore called a spinlock.

21 / 46
acquire() and release()

acquire() {
while (!available); /* busy wait */

available = false;
}

release() {
available = true;
}

22 / 46
23 / 46
pthread Mutexes

I Mutexes are represented by the pthread mutex t object.

24 / 46
pthread Mutexes

I Mutexes are represented by the pthread mutex t object.

I pthread mutex lock() locks (acquires) a pthreads mutex.

int pthread_mutex_lock(pthread_mutex_t *mutex);

24 / 46
pthread Mutexes

I Mutexes are represented by the pthread mutex t object.

I pthread mutex lock() locks (acquires) a pthreads mutex.

int pthread_mutex_lock(pthread_mutex_t *mutex);

I pthread mutex unlock() unlocks (releases) a pthreads mutex.

int pthread_mutex_unlock(pthread_mutex_t *mutex);

24 / 46
What’s The Output?
int counter = 0;
pthread_mutex_t lock;

void* thread_func(void *arg) {


pthread_mutex_lock(&lock);
counter++;
printf("Job %d started.\n", counter);
sleep(2);
printf("Job %d finished.\n", counter);
pthread_mutex_unlock(&lock);

return NULL;
}

int main(void) {
pthread_t t1, t2;

pthread_mutex_init(&lock, NULL);

pthread_create(&t1, NULL, &thread_func, NULL);


pthread_create(&t2, NULL, &thread_func, NULL);

pthread_join(t1, NULL);
pthread_join(t2, NULL);

pthread_mutex_destroy(&lock);

return 0;
}

25 / 46
What’s The Output?

Job 1 started.
Job 1 finished.
Job 2 started.
Job 2 finished.

26 / 46
Semaphores

27 / 46
Semaphore

I Synchronization tool that provides more sophisticated ways (than Mutex


locks) for process to synchronize their activities.

28 / 46
Semaphore

I Synchronization tool that provides more sophisticated ways (than Mutex


locks) for process to synchronize their activities.

I Semaphore S: integer variable.

I Accessed via two atomic operations: wait() and signal()

28 / 46
wait() and signal()

wait(S) {
while (S <= 0); // busy wait

S--;
}

signal(S) {
S++;
}

29 / 46
Counting and Binary Semaphore

I Counting semaphore: integer value can range over an unrestricted domain.

30 / 46
Counting and Binary Semaphore

I Counting semaphore: integer value can range over an unrestricted domain.

I Binary semaphore: integer value can range only between 0 and 1.


• Same as a mutex lock.

30 / 46
Semaphore Usage (1/2)

I Initialize the semaphore to the number of available resources.

31 / 46
Semaphore Usage (1/2)

I Initialize the semaphore to the number of available resources.

I Call wait() before using a resource.

31 / 46
Semaphore Usage (1/2)

I Initialize the semaphore to the number of available resources.

I Call wait() before using a resource.

I Call signal() after releasing a resource.

31 / 46
Semaphore Usage (1/2)

I Initialize the semaphore to the number of available resources.

I Call wait() before using a resource.

I Call signal() after releasing a resource.

I If S = 0: all resources are used, and processes that wish to use a resource
will block until the count becomes greater than 0.

31 / 46
Semaphore Usage (2/2)

I Consider P1 and P2 that require C1 to happen before C2.

32 / 46
Semaphore Usage (2/2)

I Consider P1 and P2 that require C1 to happen before C2.

I Create a semaphore S initialized to 0.

// Process P1
C1;
signal(S);

// Process P2
wait(S);
C2;

32 / 46
Semaphore Usage (2/2)

I Consider P1 and P2 that require C1 to happen before C2.

I Create a semaphore S initialized to 0.

// Process P1
C1;
signal(S);

// Process P2
wait(S);
C2;

I The implementation still suffers from busy waiting.

32 / 46
Semaphore Implementation with no Busy Waiting (1/2)

I With each semaphore there is an associated waiting queue.

33 / 46
Semaphore Implementation with no Busy Waiting (1/2)

I With each semaphore there is an associated waiting queue.

I Each entry in a waiting queue has two data items:


• Value (of type integer).
• Pointer to next record in the list.

33 / 46
Semaphore Implementation with no Busy Waiting (1/2)

I With each semaphore there is an associated waiting queue.

I Each entry in a waiting queue has two data items:


• Value (of type integer).
• Pointer to next record in the list.

typedef struct {
int value;
struct process *list;
} semaphore;

33 / 46
Semaphore Implementation with no Busy Waiting (2/2)

I block: place the process invoking the operation on the appropriate wait-
ing queue.
I wakeup: remove one of processes in the waiting queue and place it in
the ready queue.

34 / 46
Semaphore Implementation with no Busy Waiting (2/2)

I block: place the process invoking the operation on the appropriate wait-
ing queue.
I wakeup: remove one of processes in the waiting queue and place it in
the ready queue.
wait(semaphore *S) {
S->value--;
if (S->value < 0) {
// add this process to S->list;
block();
}
}

34 / 46
Semaphore Implementation with no Busy Waiting (2/2)

I block: place the process invoking the operation on the appropriate wait-
ing queue.
I wakeup: remove one of processes in the waiting queue and place it in
the ready queue.
wait(semaphore *S) {
S->value--;
if (S->value < 0) {
// add this process to S->list;
block();
}
}

signal(semaphore *S) {
S->value++;
if (S->value <= 0) {
// remove a process P from S->list;
wakeup(P);
}
}

34 / 46
Deadlock

I Deadlock: two or more processes are waiting indefinitely for an event


that can be caused by only one of the waiting processes.

35 / 46
Deadlock

I Deadlock: two or more processes are waiting indefinitely for an event


that can be caused by only one of the waiting processes.

I Let S and Q be two semaphores initialized to 1.

35 / 46
Starvation

I Starvation: indefinite blocking.

36 / 46
Starvation

I Starvation: indefinite blocking.

I A process may never be removed from the semaphore queue in which it


is suspended.

36 / 46
Starvation

I Starvation: indefinite blocking.

I A process may never be removed from the semaphore queue in which it


is suspended.

I If we remove processes from the list associated with a semaphore in LIFO


(last-in, first-out) order.

36 / 46
37 / 46
POSIX Semaphore

I sem open() creates a new semaphore or opens an existing one.

sem_t *sem_open(const char * name , int oflag , ...);

38 / 46
POSIX Semaphore

I sem open() creates a new semaphore or opens an existing one.

sem_t *sem_open(const char * name , int oflag , ...);

I sem wait() decrements the value of the semaphore.

int sem_wait(sem_t *sem);

38 / 46
POSIX Semaphore

I sem open() creates a new semaphore or opens an existing one.

sem_t *sem_open(const char * name , int oflag , ...);

I sem wait() decrements the value of the semaphore.

int sem_wait(sem_t *sem);

I sem post() increments the value of the semaphore.

int sem_post(sem_t *sem);

38 / 46
Parent-Child Example

void parent() {
sem_t *sem_id = sem_open(sem_name, O_CREAT, 0600, 0);

// The parent waits for its child to print


sem_wait(sem_id);
printf("Parent: Child Printed!\n");
sem_close(sem_id);
sem_unlink(sem_name);
}

39 / 46
Parent-Child Example

void parent() {
sem_t *sem_id = sem_open(sem_name, O_CREAT, 0600, 0);

// The parent waits for its child to print


sem_wait(sem_id);
printf("Parent: Child Printed!\n");
sem_close(sem_id);
sem_unlink(sem_name);
}

void child() {
sem_t *sem_id = sem_open(sem_name, O_CREAT, 0600, 0);

printf("Child: Hello parent!\n");


sem_post(sem_id);
}

39 / 46
Readers and Writers Problem

40 / 46
Readers and Writers Problem (1/3)

I A shared data set among a number of concurrent processes:


• Readers: only read the data set; they do not perform any updates.
• Writers: can both read and write.

41 / 46
Readers and Writers Problem (1/3)

I A shared data set among a number of concurrent processes:


• Readers: only read the data set; they do not perform any updates.
• Writers: can both read and write.

I Problem: allow multiple readers to read at the same time, only one single
writer can access the shared data at the same time.

41 / 46
Readers and Writers Problem (1/3)

I A shared data set among a number of concurrent processes:


• Readers: only read the data set; they do not perform any updates.
• Writers: can both read and write.

I Problem: allow multiple readers to read at the same time, only one single
writer can access the shared data at the same time.

I Shared Data
• Semaphore rw mutex initialized to 1.
• Semaphore mutex initialized to 1.
• Integer read count initialized to 0.

41 / 46
Readers and Writers Problem (2/3)

I The writer process.

do {
wait(rw_mutex);
...
/* writing is performed */
...
signal(rw_mutex);
} while (true);

42 / 46
Readers and Writers Problem (3/3)

I The reader process.

do {
wait(mutex);
read_count++;
if (read_count == 1)
wait(rw_mutex);
signal(mutex);
...
/* reading is performed */
...
wait(mutex);
read_count--;
if (read_count == 0)
signal(rw_mutex);
signal(mutex);
} while (true);

43 / 46
Summary

44 / 46
Summary

I Access to shared data

45 / 46
Summary

I Access to shared data

I The critical-section problem

45 / 46
Summary

I Access to shared data

I The critical-section problem

I Requirements: mutual-exclusion, progress, bounding waiting

45 / 46
Summary

I Access to shared data

I The critical-section problem

I Requirements: mutual-exclusion, progress, bounding waiting


I CS solutions:
• Peterson solution, mutex lock, semaphore

45 / 46
Summary

I Access to shared data

I The critical-section problem

I Requirements: mutual-exclusion, progress, bounding waiting


I CS solutions:
• Peterson solution, mutex lock, semaphore

I Reader/writer problem

45 / 46
Questions?
Acknowledgements
Some slides were derived from Avi Silberschatz slides.

46 / 46

You might also like