0% found this document useful (0 votes)
91 views

Thread Syncronization: Chapter 13-14 of Robbins Book

The document discusses thread synchronization techniques including mutexes, semaphores, and monitors. It focuses on mutexes and POSIX thread synchronization, describing how to create, initialize, lock, unlock and destroy mutexes. It also covers using mutexes to protect shared resources, and how signals are handled differently for threads through functions like pthread_sigmask and pthread_kill.

Uploaded by

elemaniaq
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
91 views

Thread Syncronization: Chapter 13-14 of Robbins Book

The document discusses thread synchronization techniques including mutexes, semaphores, and monitors. It focuses on mutexes and POSIX thread synchronization, describing how to create, initialize, lock, unlock and destroy mutexes. It also covers using mutexes to protect shared resources, and how signals are handled differently for threads through functions like pthread_sigmask and pthread_kill.

Uploaded by

elemaniaq
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 27

Lecture 10

Chapter 13-14 of Robbins Book

Thread Syncronization

BIL 244 – System Programming


Overview

• To 'protect' an area of code that is a 'critical section'


basically 3 different methods are used.
– mutex,
– semaphore,
– monitor

• The implementation of each is very different but


mostly
– mutex can be implemented in hardware
– semaphores used by low level languages
– monitors used by high level languages

BIL 244 – System Programming


Overview

• At its most basic a semaphore is a struct consisting


of:
– data (an integer indicating the semaphore state)
– operation (either of wait/signal)
• If the data is only allowed to take values of 0 or 1, this
is a binary semaphore – i.e. a mutex
• If the data is allowed to take any non-zero value, this is
known as a general semaphore
• If the semaphore has a value of zero, any process that
tries to decrement it (i.e. obtain the lock) will be
blocked until the semaphore has a non-zero value
BIL 244 – System Programming
POSIX Syncronization

• The POSIX thread library contains several


synchronization constructs.

• The simplest of these is the mutex lock.

• A mutex, or mutex lock, is a special variable that can be


either in the locked state or the unlocked state.

BIL 244 – System Programming


Mutex Locks

• If the mutex is locked, it has a distinguished thread that


holds or owns the mutex.
• If no thread holds the mutex, we say the mutex is
unlocked, free or available.
• The mutex also has a queue for the threads that are
waiting to hold the mutex.
• The order in which the threads in the mutex queue obtain
the mutex is determined by the thread-scheduling policy
• POSIX does not require that any particular policy be
implemented (does not require that this queue be
accessed FIFO).
BIL 244 – System Programming
Mutex Locks

• A mutex is meant to be held for only a short period of


time.
• It is usually used to protect access to a shared variable.
idea:
lock the mutex
critical section
unlock the mutex
• Only the owner of the mutex should unlock the mutex.
• Do not lock a mutex that is already locked.
• Do not unlock a mutex that is not already locked.

BIL 244 – System Programming


Creating and initializing a mutex

• We will only cover the simplest (and most often used)


method of using POSIX mutexes
• Since a mutex is meant to be used by multiple threads, it
is usually declared to have static storage class.
• It can be defined inside a function using the static
qualifier if it will only be used by that function or it can
be defined at the top level.
• A mutex must be initialized before it is used.
• This can be done when the mutex is defined, as in:
pthread_mutex_t mymutex =PTHREAD_MUTEX_INITIALIZER;

BIL 244 – System Programming


Creating and initializing a mutex

• The mutex parameter of pthread_mutex_init is a pointer


to the mutex to be initialized. Pass NULL for the attr
parameter of pthread_mutex_init to initialize a mutex
with the default attributes. Otherwise, first create and
initialize a mutex attribute object in a manner similar to
that used for thread attribute objects.
#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *restrict mutex,


const pthread_mutexattr_t *restrict attr);

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

BIL 244 – System Programming


Destroying a mutex

• The pthread_mutex_destroy function destroys the


mutex referenced by its parameter. The mutex parameter is
a pointer to the mutex to be destroyed.
• A pthread_mutex_t variable that has been destroyed
with pthread_mutex_destroy can be reinitialized with
pthread_mutex_init.

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);

BIL 244 – System Programming


Locking and unlocking a mutex

• POSIX has two functions, pthread_mutex_lock and


pthread_mutex_trylock for acquiring a mutex.
• The pthread_mutex_lock function blocks until the mutex is
available, while the pthread_mutex_trylock always returns
immediately.
• The pthread_mutex_unlock function releases the specified mutex.
• All three functions take a single parameter, mutex, a pointer to 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);

BIL 244 – System Programming


The following code segment uses a mutex to protect a critical section

pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;


pthread_mutex_lock(&mylock);
/* critical section */
pthread_mutex_unlock(&mylock);
A counter that can be accessed by multiple threads

#include <pthread.h>
static int count = 0;
static pthread_mutex_t countlock = PTHREAD_MUTEX_INITIALIZER;

int increment(void) { /* increment the counter */


int error;
if (error = pthread_mutex_lock(&countlock))
return error;
count++;
return pthread_mutex_unlock(&countlock);
}

int decrement(void) { /* decrement the counter */


int error;
if (error = pthread_mutex_lock(&countlock))
return error;
count--;
return pthread_mutex_unlock(&countlock);
}

int getcount(int *countp) { /* retrieve the counter */


int error;
if (error = pthread_mutex_lock(&countlock))
return error;
*countp = count;
return pthread_mutex_unlock(&countlock);
}
At-Most-Once and At-Least-Once-
Execution
• If a mutex isn't statically initialized, the program must call
pthread_mutex_init before using any of the other mutex
functions.
• Care must be taken to call pthread_mutex_init before any thread
accesses a mutex, but having each thread initialize the mutex doesn't
work either. The effect of calling pthread_mutex_init for a mutex
that has already been initialized is not defined.
• The notion of single initialization is so important that POSIX
provides the pthread_once function to ensure these semantics.
• The once_control parameter must be statically initialized with
PTHREAD_ONCE_INIT.
• The init_routine is called the first time pthread_once is called with
a given once_control, and init_routine is not called on
subsequent calls. When a thread returns from pthread_once
without error, the init_routine has been completed by some
thread.
BIL 244 – System Programming
At-Most-Once and At-Least-Once-
Execution

#include <pthread.h>
int pthread_once(pthread_once_t *once_control,
void (*init_routine)(void));
pthread_once_t once_control = PTHREAD_ONCE_INIT;

• If successful, pthread_once returns 0. If


unsuccessful, pthread_once returns a nonzero error
code.
• No mandatory errors are defined for pthread_once .

BIL 244 – System Programming


Condition Variables

• This part is left to the Students

BIL 244 – System Programming


Signal Handling and Threads

• All threads in a process share the process signal handlers, but each
thread has its own signal mask.
• The interaction of threads with signals involves several
complications because threads can operate asynchronously with
signals
• Signals such as SIGFPE (floating-point exception) are synchronous to
the thread that caused them (i.e., they are always generated at the
same point in the thread's execution)
• Other signals are asynchronous because they are not generated at a
predictable time nor are they associated with a particular thread
• If several threads have an asynchronous signal unblocked, the
thread runtime system selects one of them to handle the signal.
• Signals can also be directed to a particular thread with
pthread_kill.

BIL 244 – System Programming


Directing a signal to a particular
thread
• The pthread_kill function requests that signal number sig be
generated and delivered to the thread specified by thread.
#include <signal.h>
#include <pthread.h>

int pthread_kill(pthread_t thread, int sig);


• If successful, pthread_kill returns 0. If unsuccessful,
pthread_kill returns a nonzero error code. In the latter case, no
signal is sent
• Although pthread_kill delivers the signal to a particular thread,
the action of handling it may affect the entire process. A common
confusion is to assume that pthread_kill always causes process
termination (but this is not the case)
• The pthread_kill just causes a signal to be generated for the
thread
BIL 244 – System Programming
Masking signals for threads

• While signal handlers are process-wide, each thread has


its own signal mask.
• A thread can examine or set its signal mask with the
pthread_sigmask function, which is a generalization
of sigprocmask to threaded programs
• The sigprocmask function should not be used when
the process has multiple threads, but it can be called by
the main thread before additional threads are created

BIL 244 – System Programming


Masking signals for threads

• The how and set parameters specify the way the signal mask is
to be modified.
• If the oset parameter is not NULL, the pthread_sigmask
function sets *oset to the thread's previous signal mask.
#include <signal.h>
#include <pthread.h>

int pthread_sigmask(int how, const sigset_t *restrict set,


sigset_t *restrict oset);

• If successful, pthread_sigmask returns 0. If unsuccessful,


pthread_sigmask returns a nonzero error code.
• The pthread_sigmask function returns EINVAL if how is not
valid.

BIL 244 – System Programming


Dedicating threads for signal
handling
• A recommended strategy for dealing with signals in
multithreaded processes is to dedicate particular threads to
signal handling
• The main thread blocks all signals before creating the
threads.
• The signal mask is inherited from the creating thread, so all
threads have the signal blocked.
• The thread dedicated to handling the signal then executes
sigwait on that signal
• Alternatively, the thread can use pthread_sigmask to
unblock the signal. The advantage of using sigwait is that
the thread is not restricted to async-signal-safe functions.
BIL 244 – System Programming
Readers and Writers

• The reader-writer problem refers to a situation in which


a resource allows two types of access (reading and
writing).
• One type of access must be granted exclusively (e.g.,
writing), but the other type may be shared (e.g.,
reading).
• For example, any number of processes can read from
the same file without difficulty, but only one process
should modify the file at a time.

BIL 244 – System Programming


Readers and Writers

• Two common strategies for handling reader-writer


synchronization are called strong reader synchronization
and strong writer synchronization
• Strong reader synchronization always gives preference to
readers, granting access to readers as long as a writer is not
currently writing
• Strong writer synchronization always gives preference to
writers, delaying readers until all waiting or active writers
complete
• An airline reservation system would use strong writer
preference, since readers need the most up-to-date
information. On the other hand, a library reference
database might want to give readers preference.

BIL 244 – System Programming


Readers and Writers

• POSIX provides read-write locks that allow multiple


readers to acquire a lock, provided that a writer does not
hold the lock.
• POSIX states that it is up to the implementation whether
to allow a reader to acquire a lock if writers are blocked
on the lock.
• POSIX read-write locks are represented by variables of
type pthread_rwlock_t.
• Programs must initialize pthread_rwlock_t variables
before using them for synchronization by calling
pthread_rwlock_init.
BIL 244 – System Programming
Readers and Writers

• The rwlock parameter is a pointer to a read-write lock.


• Pass NULL for the attr parameter of pthread_rwlock_init
to initialize a read-write lock with the default attributes.
Otherwise, first create and initialize a read-write lock
attribute object in a manner similar to that used for thread
attribute objects.
#include <pthread.h>

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,


const pthread_rwlockattr_t *restrict attr);

BIL 244 – System Programming


Readers and Writers

• The pthread_rwlock_destroy function destroys the


read-write lock referenced by its parameter
• The rwlock parameter is a pointer to a read-write lock. A
pthread_rwlock_t variable that has been destroyed with
pthread_rwlock_destroy can be reinitialized with
pthread_rwlock_init.

#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)

• If successful, pthread_rwlock_destroy returns 0. If


unsuccessful, it returns a nonzero error code
BIL 244 – System Programming
Readers and Writers

• The pthread_rwlock_rdlock and pthread_rwlock_tryrdlock


functions allow a thread to acquire a read-write lock for reading.
• The pthread_rwlock_wrlock and pthread_rwlock_trywrlock
functions allow a thread to acquire a read-write lock for writing.
• The pthread_rwlock_rdlock and pthread_rwlock_wrlock
functions block until the lock is available, whereas
pthread_rwlock_tryrdlock and pthread_rwlock_trywrlock
return immediately.
• The pthread_rwlock_unlock function causes the lock to be
released.
• These functions require that a pointer to the lock be passed as a
parameter.

BIL 244 – System Programming


Readers and Writers

#include <pthread.h>

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);


int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

• If successful, these functions return 0. If unsuccessful, these


functions return a nonzero error code.
• The pthread_rwlock_tryrdlock and
pthread_rwlock_trywrlock functions return EBUSY if the
lock could not be acquired because it was already held.

BIL 244 – System Programming

You might also like