0% found this document useful (0 votes)
53 views42 pages

15 Sem Mutex

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)
53 views42 pages

15 Sem Mutex

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/ 42

Synchronization and

Semaphores

Copyright ©: University of Illinois CS 241 Staff 1


Synchronization Primatives

 Counting Semaphores
 Permit a limited number of threads to execute a
section of the code
 Binary Semaphores - Mutexes
 Permit only one thread to execute a section of
the code
 Condition Variables
 Communicate information about the state of
shared data

Copyright ©: University of Illinois CS 241 Staff 2


POSIX Semaphores
 Named Semaphores
 Provides synchronization between unrelated process and
related process as well as between threads
 Kernel persistence
 System-wide and limited in number
 Uses sem_open
 Unnamed Semaphores
 Provides synchronization between threads and between
related processes
 Thread-shared or process-shared
 Uses sem_init

Copyright ©: University of Illinois CS 241 Staff 3


POSIX Semaphores

 Data type
 Semaphore is a variable of type sem_t
 Include <semaphore.h>
 Atomic Operations
int sem_init(sem_t *sem, int pshared,
unsigned value);
int sem_destroy(sem_t *sem);
int sem_post(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_wait(sem_t *sem);
Copyright ©: University of Illinois CS 241 Staff 4
Unnamed Semaphores
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned
value);
 Initialize an unnamed semaphore
 Returns You cannot make a copy of a
 0 on success semaphore variable!!!
 -1 on failure, sets errno
 Parameters
 sem:
 Target semaphore
 pshared:
 0: only threads of the creating process can use the semaphore
 Non-0: other processes can use the semaphore
 value:
 Initial value of the semaphore
Copyright ©: University of Illinois CS 241 Staff 5
Sharing Semaphores
 Sharing semaphores between threads
within a process is easy, use pshared==0
 A non-zero pshared allows any process
that can access the semaphore to use it
 Places the semaphore in the global (OS)
environment
 Forking a process creates copies of any
semaphore it has
 Note: unnamed semaphores are not shared across
unrelated processes

Copyright ©: University of Illinois CS 241 Staff 6


sem_init can fail

 On failure
 sem_init returns -1 and sets errno
errno cause
EINVAL Value > sem_value_max
ENOSPC Resources exhausted
EPERM Insufficient privileges

sem_t semA;

if (sem_init(&semA, 0, 1) == -1)
perror(“Failed to initialize semaphore semA”);
Copyright ©: University of Illinois CS 241 Staff 7
Semaphore Operations
#include <semaphore.h>
int sem_destroy(sem_t *sem);
 Destroy an semaphore
 Returns
 0 on success
 -1 on failure, sets errno
 Parameters
 sem:
 Target semaphore
 Notes
 Can destroy a sem_t only once
 Destroying a destroyed semaphore gives undefined results
 Destroying a semaphore on which a thread is blocked gives undefined
results

Copyright ©: University of Illinois CS 241 Staff 8


Semaphore Operations
#include <semaphore.h>
int sem_post(sem_t *sem);
 Unlock a semaphore - same as signal
 Returns
 0 on success
 -1 on failure, sets errno (== EINVAL if semaphore doesn’t exist)
 Parameters
 sem:
 Target semaphore
 sem > 0: no threads were blocked on this semaphore, the semaphore
value is incremented
 sem == 0: one blocked thread will be allowed to run
 Notes
 sem_post() is reentrant with respect to signals and may be invoked from
a signal-catching function
Copyright ©: University of Illinois CS 241 Staff 9
Semaphore Operations
#include <semaphore.h>
int sem_wait(sem_t *sem);
 Lock a semaphore
 Blocks if semaphore value is zero
 Returns
 0 on success
 -1 on failure, sets errno (== EINTR if interrupted by a signal)
 Parameters
 sem:
 Target semaphore
 sem > 0: thread acquires lock
 sem == 0: thread blocks

Copyright ©: University of Illinois CS 241 Staff 10


Semaphore Operations
#include <semaphore.h>
int sem_trywait(sem_t *sem);
 Test a semaphore’s current condition
 Does not block
 Returns
 0 on success
 -1 on failure, sets errno (== AGAIN if semaphore already locked)
 Parameters
 sem:
 Target semaphore
 sem > 0: thread acquires lock
 sem == 0: thread returns

Copyright ©: University of Illinois CS 241 Staff 11


Example: bank balance

 Protect shared variable


balance with a
semaphore when used in:
 decshared
 Decrements current value of
balance
 incshared
 increments the balance

Copyright ©: University of Illinois CS 241 Staff 12


Example: bank balance
int decshared() {
while (sem_wait(&balance_sem) == -1)
if (errno != EINTR)
return -1;
balance--;
return sem_post(&balance_sem);
}

int incshared() {
while (sem_wait(&balance_sem) == -1)
if (errno != EINTR)
return -1;
balance++;
return sem_post(&balance_sem);
}
Copyright ©: University of Illinois CS 241 Staff 13
Example: bank balance
#include <errno.h>
#include <semaphore.h>

static int balance = 0;


static sem_t bal_sem; pshared

int initshared(int val) {


if (sem_init(&bal_sem, 0, 1) == -1)
return -1;
balance = val;
return 0;
} value

Copyright ©: University of Illinois CS 241 Staff 14


Example: bank balance
int decshared() { int incshared() {
while (sem_wait(&bal_sem) while (sem_wait(&bal_sem)
== -1) == -1)
if (errno != EINTR) if (errno != EINTR)
return -1; return -1;
balance--; balance++;
return sem_post(&bal_sem); return sem_post(&bal_sem);
} }

Which one is going first?

Copyright ©: University of Illinois CS 241 Staff 15


Advanced Semaphores
int semget(key_t key, int nsems, int semflg);

 Get set of semaphores


int semop(int semid, struct sembuf *sops,
unsigned int nsops);

 Atomically perform a user-defined


array of semaphore operations on the
set of semaphores

Copyright ©: University of Illinois CS 241 Staff 16


Pthread Synchronization

 Mutex  When several


 Semaphore with threads compete
maximum value 1  One wins
 Simple and efficient  The rest block
 States  Queue of blocked
threads
 Locked
 Some thread holds
the mutex
 Unlocked
 No thread holds the
mutex
Copyright ©: University of Illinois CS 241 Staff 17
Mutex Variables

 A typical sequence in the use of a mutex


1. Create and initialize mutex
2. Several threads attempt to lock mutex
3. Only one succeeds and now owns mutex
4. The owner performs some set of actions
5. The owner unlocks mutex
6. Another thread acquires mutex and repeats the
process
7. Finally mutex is destroyed

Copyright ©: University of Illinois CS 241 Staff 18


Creating a mutex
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr);
 Initialize a pthread mutex: the mutex is initially unlocked
 Returns
 0 on success
 Error number on failure
 EAGAIN: The system lacked the necessary resources; ENOMEM: Insufficient
memory ; EPERM: Caller does not have privileges; EBUSY: An attempt to re-
initialise a mutex; EINVAL: The value specified by attr is invalid

 Parameters
 mutex: Target mutex
 attr:
 NULL: the default mutex attributes are used
 Non-NULL: initializes with specified attributes
Copyright ©: University of Illinois CS 241 Staff 19
Creating a mutex

 Default attributes
 Use PTHREAD_MUTEX_INITIALIZER
 Statically allocated
 Equivalent to dynamic initialization by a call
to pthread_mutex_init() with parameter
attr specified as NULL
 No error checks are performed

Copyright ©: University of Illinois CS 241 Staff 20


Destroying a mutex
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t
*mutex);
 Destroy a pthread mutex
 Returns
 0 on success
 Error number on failure
 EBUSY: An attempt to re-initialise a mutex; EINVAL: The value specified by
attr is invalid
 Parameters
 mutex: Target mutex

Copyright ©: University of Illinois CS 241 Staff 21


Locking/unlocking a mutex
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t
*mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
 Returns
 0 on success
 Error number on failure
 EBUSY: already locked; EINVAL: Not an initialised mutex; EDEADLK: The
current thread already owns the mutex; EPERM: The current thread does not
own the mutex

Copyright ©: University of Illinois CS 241 Staff 22


Simple Example
#include <pthread.h> int main (int argc, char *argv[]) {
#include <stdio.h> pthread_t thread[2];
#include <stdlib.h>
pthread_create(&thread[0], NULL,
static pthread_mutex_t my_lock = mythread, (void *)0);
PTHREAD_MUTEX_INITIALIZER;
pthread_create(&thread[1], NULL,
void *mythread(void *ptr) { mythread, (void *)1);
long int i,j;
while (1) { getchar();
}
pthread_mutex_lock (&my_lock);

for (i=0; i<10; i++) {


printf ("Thread %d\n", int) ptr);
for (j=0; j<50000000; j++);
}

pthread_mutex_unlock (&my_lock);
for (j=0; j<50000000; j++);
}
} Copyright ©: University of Illinois CS 241 Staff 23
Condition Variables

 Used to communicate information about the


state of shared data
 Execution of code depends on the state of
 A data structure or
 Another running thread
 Allows threads to synchronize based upon
the actual value of data
 Without condition variables
 Threads continually poll to check if the condition
is met
Copyright ©: University of Illinois CS 241 Staff 24
Condition Variables

 Signaling, not mutual exclusion


 A mutex is needed to synchronize access to the
shared data
 Each condition variable is associated with a
single mutex
 Wait atomically unlocks the mutex and blocks
the thread
 Signal awakens a blocked thread

Copyright ©: University of Illinois CS 241 Staff 25


Creating a Condition Variable

 Similar to pthread mutexes


int pthread_cond_init(pthread_cond_t *cond, const
pthread_condattr_t *attr);
int pthread_cond_destroy(pthread_cond_t *cond);

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

Copyright ©: University of Illinois CS 241 Staff 26


Using a Condition Variable

 Waiting
 Block on a condition variable.
 Called with mutex locked by the calling thread
 Atomically release mutex and cause the calling
thread to block on the condition variable
 On return, mutex is locked again
int pthread_cond_wait(pthread_cond_t *cond,
pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond,
pthread_mutex_t *mutex, const struct timespec
*abstime);
Copyright ©: University of Illinois CS 241 Staff 27
Using a Condition Variable

 Signaling
int pthread_cond_signal(pthread_cond_t *cond);
 unblocks at least one of the blocked threads
int pthread_cond_broadcast(pthread_cond_t *cond);
 unblocks all of the blocked threads

 Signals are not saved


 Must have a thread waiting for the signal or it will
be lost

Copyright ©: University of Illinois CS 241 Staff 28


Condition Variable: Why do we
need the mutex?
pthread_mutex_lock(&mutex); /* lock mutex */
while (!predicate) { /* check predicate */

pthread_cond_wait(&condvar, &mutex);
/* go to sleep – recheck
pred on awakening */
}
pthread_mutex_unlock(&mutex); /* unlock mutex */

pthread_mutex_lock(&mutex); /* lock the mutex */


predicate=1; /* set the predicate */
pthread_cond_broadcast(&condvar); /* wake everyone up */
pthread_mutex_unlock(&mutex); /* unlock the mutex */

Copyright ©: University of Illinois CS 241 Staff 29


Condition Variable: No mutex!
pthread_mutex_lock(&mutex); /* lock mutex */
while (!predicate) { /* check predicate */
pthread_mutex_unlock(&mutex); /* unlock mutex */
pthread_cond_wait(&condvar); /* go to sleep – recheck
pred on awakening */
pthread_mutex_lock(&mutex); /* lock mutex */
}
pthread_mutex_unlock(&mutex); /* unlock mutex */

What can happen here?

pthread_mutex_lock(&mutex); /* lock the mutex */


predicate=1; /* set the predicate */
pthread_cond_broadcast(&condvar); /* wake everyone up */
pthread_mutex_unlock(&mutex); /* unlock the mutex */

Copyright ©: University of Illinois CS 241 Staff 30


Using a Condition Variable:
Challenges
 Call pthread_cond_signal() before calling
pthread_cond_wait()
 Logical error
 Fail to lock the mutex before calling
pthread_cond_wait()
 May cause it NOT to block
 Fail to unlock the mutex after calling
pthread_cond_signal()
 May not allow a matching pthread_cond_wait()
routine to complete (it will remain blocked).

Copyright ©: University of Illinois CS 241 Staff 35


Example without Condition
Variables
int data_avail = 0;
pthread_mutex_t data_mutex =
PTHREAD_MUTEX_INITIALIZER;

void *producer(void *) {
pthread_mutex_lock(&data_mutex);

<Produce data>
<Insert data into queue;>
data_avail=1;

pthread_mutex_unlock(&data_mutex);
}

Copyright ©: University of Illinois CS 241 Staff 36


Example without Condition
Variables
void *consumer(void *) {
while( !data_avail ); /* do nothing */

pthread_mutex_lock(&data_mutex);
Busy Waiting!
<Extract data from queue;>
if (queue is empty)
data_avail = 0;

pthread_mutex_unlock(&data_mutex);
<Consume Data>
}

Copyright ©: University of Illinois CS 241 Staff 37


Example with Condition
Variables
int data_avail = 0;
pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cont_t data_cond = PTHREAD_COND_INITIALIZER;

void *producer(void *) {
pthread_mutex_lock(&data_mutex);
<Produce data>
<Insert data into queue;>
data_avail = 1;

pthread_cond_signal(&data_cond);
pthread_mutex_unlock(&data_mutex);
}

Copyright ©: University of Illinois CS 241 Staff 38


Example with Condition
Variables
void *consumer(void *) {
pthread_mutex_lock(&data_mutex);
while( !data_avail ) {
/* sleep on condition variable*/
pthread_cond_wait(&data_cond, &data_mutex);
}
/* woken up */
<Extract data from queue;> No Busy Waiting!
if (queue is empty)
data_avail = 0;
pthread_mutex_unlock(&data_mutex);
<Consume Data>
}

Copyright ©: University of Illinois CS 241 Staff 39


More Complex Example

 Master thread
 Spawns a number of concurrent slaves
 Waits until all of the slaves have finished to exit
 Tracks current number of slaves executing
 A mutex is associated with count and a
condition variable with the mutex

Copyright ©: University of Illinois CS 241 Staff 40


Example
#include <stdio.h>
#include <pthread.h>

#define NO_OF_PROCS 4

typedef struct _SharedType {


int count; /* number of active slaves */
pthread_mutex_t lock; /* mutex for count */
pthread_cond_t done; /* sig. by finished slave */
} SharedType, *SharedType_ptr;

SharedType_ptr shared_data;

Copyright ©: University of Illinois CS 241 Staff 41


Example: Main
main(int argc, char **argv) { /* allocate condition var */
int res; if ((res =
/* allocate shared data */ pthread_cond_init(&sh_data-
if ((sh_data = (SharedType *) >done, NULL)) != 0) {
malloc(sizeof(SharedType))) == exit(1);
NULL) { }
exit(1);
} /* generate number of slaves
sh_data->count = 0; to create */
srandom(0);
/* allocate mutex */ /* create up to 15 slaves */
if ((res = master((int) random()%16);
pthread_mutex_init(&sh_data- }
>lock, NULL)) != 0) {
exit(1);
}
Copyright ©: University of Illinois CS 241 Staff 42
Example: Slave
void slave(void *shared) { /* done running */
int i, n; printf("Slave finished %d
sh_data = shared; cycles.\n", n);
printf(“Slave.\n", n);
n = random() % 1000; /* signal that you are done
working */
for (i = 0; i < n; i+= 1) pthread_cond_signal(&sh_data-
>done);
Sleep(10);

/* release mutex for shared


/* mutex for shared data */ data */
pthread_mutex_lock(&sh_data- pthread_mutex_unlock(&sh_data-
>lock); >lock);
}
/* dec number of slaves */
sh_data->count -= 1;
Copyright ©: University of Illinois CS 241 Staff 43
Example: Master
master(int nslaves) { pthread_mutex_lock(&sh_data-
int i; >lock);
pthread_t id;
for (i = 1; i <= nslaves; i += while (sh_data->count != 0)
1) { pthread_cond_wait(&sh_data-
pthread_mutex_lock(&sh_data- >done, &sh_data->lock);
>lock);
/* start slave and detach */ pthread_mutex_unlock(&sh_data-
shared_data->count += 1; >lock);
pthread_create(&id, NULL,
(void* (*)(void *))slave, printf("All %d slaves have
(void *)sh_data); finished.\n", nslaves);
pthread_mutex_unlock(&sh_data- pthread_exit(0);
>lock); }
}

Copyright ©: University of Illinois CS 241 Staff 44


Semaphores vs. CVs
Semaphore Condition Variables
 Integer value (>=0)  No integer value
 Wait does not always  Wait always blocks
block
 Signal either releases  Signal either releases
thread or inc’s counter thread or is lost
 If signal releases  If signal releases
thread, both threads thread, only one of
continue afterwards them continue

Copyright ©: University of Illinois CS 241 Staff 45


Dining Philosophers
 N philosophers and N forks
 Philosophers eat/think
 Eating needs 2 forks
 Pick one fork at a time

Copyright ©: University of Illinois CS 241 Staff 46

You might also like