You are on page 1of 14

07/11/2018

Process
 Processes do not share memory!
 Different processes
 execute different code;
 access distinct memory spaces.
 Processes can communicate through a message passing
mechanism:

P1 P2
channel

data data

Outline Process and threads


 A process can include several concurrent sub‐processes, 
1. General descriptions called threads.
2. Thread management  Threads belonging to the same process:
3. Scheduler(s) in Linux  share the same memory space;
 have distinct stacks;
4. Time management  can execute the same code. 1 2 3 Pi
5. Handling periodic threads
Example of a process
6. Mutual exclusion
composed by 3 threads:
7. Examples
data

Process Pthread Library


It includes a set of functions for thread management:
A process is the main execution entity managed by
the operating system for executing a program on a  pthread_create: create a thread
specific set of data.
 pthread_exit: terminate the calling thread
 The following things are needed by the OS to manage a  pthread_cancel: terminate another thread
process:  pthread_join: wait for the termination of a thread
 code: compiled set of instructions;
 data: memory space for global variables; Compiling
 stack: memory space for local variables;
To use the Pthread library, insert #include <pthread.h>
 context: status of CPU registers;
 parameters: type, priority, arguments.  gcc try.c -o try -lpthread -lrt

1
07/11/2018

Thread creation Thread creation


int pthread_create(thread_t *id, Main thread thread th1
pthread_attr_t *attr,
pthread_t th1;
void *(*body)(void *),
void *arg);
Creates a thread and returns 0 on success, or an error pthread_create(&th1,…
code, otherwise:
id contains the identifier of the created thread.
attr pointer to a structure that contains the thread attributes A create thread may not be immediately active. After a
(if NULL uses default values). create one of the following things is true:
body pointer to the function defining the thread code.  the thread is not active yet;
arg pointer to a single argument of the thread. To have more
 the thread in the ready queue (thus active);
arguments, pass a pointer to a structure.  the thread already terminated.

Thread creation Thread termination

Example: A thread can terminate for different causes:


Thread ID use default no • when it executes the last instruction of the associated
attributes argument function (normal termination);
pthread_t tid;
pthread_create(&tid, NULL, task, NULL); • when it calls the pthread_exit function;

• when another thread executes pthread_cancel.


void *task()
{ • when its father process (e.g., the main() process)
printf("I am a simple thread.\n"); terminates for a normal termination or for a call to the
} exit function.

Thread ID Thread termination


Since the thread identifier is returned to the thread creator, void pthread_exit(void *retval);
the following functions are provided:
Terminate the calling thread and put in *retval the value
 pthread_self() returned by the terminated thread.
returns the ID of the calling thread
int pthread_cancel(pthread_t th);
 pthread_equal(tid1, tid2)
returns a value  0 if tid1 = tid2, 0 otherwise. Send a cancelation request for thread th. The actual
termination depends on two attributes of the thread:
pthread_t tid1, tid2; state: enabled (default) or disabled
tid1 = pthread_self(); type: asynchronous or deferred (default)
if (pthread_equal(tid1,tid2)) that can be set by the following functions
return 0;
else return 1; pthread_setcancelstate and pthread_setcanceltype

2
07/11/2018

Thread joining Joinable vs. detached


It is possible to wait for the termination of a thread When calling pthread_join the thread may be already
through the function pthread_join: terminated, hence the system must keep some information
even after termination.
int pthread_join(pthread_t th, void **retval);
 Such an information is destroyed (and the memory
Wait for the termination of thread th. reclaimed) with the join operation.
• If retval  NULL, the return value of the terminated  If we do not need to synchronize with a thread and we
thread is copied in *retval. do not call pthread_join, the memory is not reclaimed.
• If the thread is already terminated, *retval gets the  To avoid wasting memory when joining is not needed, a
value PTHREAD_CANCELED. thread can be declared as detached (by default, a
• It returns 0 in case of success, or an error code. thread is joinable).

Example - create Joinable vs. detached


#include <pthread.h>
There are two ways to define a thread as detached:
void *task(void *p);
int main() The 2 threads have the same code,  Use the detachstate attribute during creation.
{
but they can be made different
pthread_t tid1, tid2;
int tret1, tret2; through the passed parameter.  Call the function pthread_detach().
int a = 1, b = 2;
tret1 = pthread_create(&tid1, NULL, task, (void*)&a);
tret2 = pthread_create(&tid2, NULL, task, (void*)&b);
When a detached thread is terminated, the system
pthread_join(tid1, NULL);
automatically reclaims all the data structures used for it.
pthread_join(tid2, NULL);
printf("Thread1 returns %d\n", tret1);
printf("Thread2 returns %d\n", tret2);
return 0;
}

Example - create Detached threads


void *task(void *p) int pthread_detach(pthread_t th);
{
int *pi; It defines thread th as detached. It returns 0 in case of
success, or a code error.
pi = (int *)p;
printf("This is TASK %d\n", *pi); Such a function can also be called by the thread that is
} going to be detached.
In such a case, the thread identifier can be retrieved by the
function pthread_self():
pthread_t my_id;
my_id = pthread_self();
pthread_detach(my_id);

3
07/11/2018

Thread attributes Thread priority


Specify the characteristics of a thread: The priority of a thread is specified through a structure
containing a single field:
stack size dimension of the stack memory.
struct sched_param {
state type (joinable or detached). int sched_priority;
};
priority thread priority level.
The priority must be first assigned through a local structure
scheduler algorithm to be used for scheduling the
and then inserted in the thread attributes by a proper
thread. function:
Such attributes must be initialized and destroyed: struct sched_param mypar;
int pthread_attr_init(pthread_attr_t *attr); mypar.sched_priority = 23;
int pthread_attr_destroy(pthread_attr_t *attr); pthread_attr_setschedparam(&myatt, &mypar);

Thread state Linux Schedulers


The state of the thread that is going to be created is One of the following schedulers can be used for a thread:
defined by modifying the default attributes through the SCHED_OTHER Round Robin scheduler with aging
function pthread_attr_setdetachstate(): mechanism (default policy).
SCHED_FIFO Priority scheduler: threads with same
pthread_t tid; priority are managed by FIFO.
pthread_attr_t attr;
SCHED_RR Priority scheduler: threads with same
priority are managed by Round‐Robin.
pthread_attr_init(&attr);
SCHED_DEADLINE Reservation scheduler implemented as
pthread_attr_setdetachstate(&attr,
a set of CBS running on top of EDF.
PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, task, NULL); SCHED_FIFO and SCHED_RR and SCHED_DEADLINE are
called real‐time policies and can only be used from root.

Thread stack Real-Time policies


The size of the stack of a created thread can be set by SCHED_FIFO (Fixed‐Priority Scheduling + FIFO)
pthread_attr_setstacksize() and can retrieved by function Priority scheduler: threads with the same priority are
pthread_attr_getstacksize() (the default value is 2 MB): handled by a FIFO policy. A thread is executed until
termination, cancellation, blocking, or preemption.
#define STK_SIZE 40960 // 4 MB
SCHED_RR (Fixed‐Priority Scheduling + RR)
pthread_t tid;
pthread_attr_t attr; Priority scheduler: threads with the same priority are
void *stack; handled by a Round‐Robin policy. A thread is executed
pthread_attr_init(&attr); until termination, cancellation, blocking, preemption, or
exhaustion of the time quantum.
pthread_attr_setstacksize(&attr,
&stack, STK_SIZE); The time quantum depends on the system and cannot
be defined by the user, but it can be retrieved by calling
pthread_create(&tid, &attr, task, NULL);
the function sched_rr_get_interval().

4
07/11/2018

Round-Robin quantum Setting the scheduler


int sched_rr_get_interval(pid_t pid, struct timespec *tp); The scheduler is also specified through the attributes, by
calling the function pthread_attr_setschedpolicy.
The value of the time quantum used by the Round‐Robin
scheduler for the process identified by pid is copied in the  By default, a thread is scheduled with the same policy
structure pointed by tp. used for the father (SCHED_OTHER for the main).
If pid = 0, it returns the time quantum used for the calling  The use of different policies must be notified to the
process: kernel by the function pthread_attr_setinheritsched:
#include <sched.h>
pthread_attr_t myatt;
struct timespec q;
pthread_attr_init(&myatt);
sched_rr_get_interval(0, &q); pthread_attr_setinheritsched(&myatt, PTHREAD_EXPLICIT_SCHED);
printf("Q: %ld s, %ld ns\n", pthread_attr_setschedpolicy(&myatt, SCHED_FIFO);
q.tv_sec, q.tv_nsec);

Priority Schedulers Example - create


 Linux allows 99 priority levels: 1 (low), 99 (high). int main()
However, the POSIX standard requires only 32 levels. {
pthread_attr_t myatt; /* attribute structure */
 For each priority level there is a queue, in which all struct sched_param mypar; /* priority structure */
threads with the same priority are inserted. pthread_t tid; /* thread id */

 The thread at the head of the highest priority queue is pthread_attr_init(&myatt);


selected as a running task: pthread_attr_setinheritsched(&myatt, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&myatt, SCHED_FIFO);

priority mypar.sched_priority = 23;


pthread_attr_setschedparam(&myatt, &mypar);

CPU err = pthread_create(&tid, &myatt, task, NULL);

pthread_join(tid, NULL);
pthread_attr_destroy(&myatt);
}

Linux Schedulers Temporal protection


nice value
Temporal isolation
To prevent erroneous applications blocking the system,
-20 19
priority priority there is a temporal protection mechanism that guarantees
(Q,P) (Q,P) (Q,P)
99 99
CBS CBS CBS
a minimum execution time to the system.
FIFO RR
Such a mechanism is configurable by 2 parameters:

SCHED_OTHER EDF
/proc/sys/kernel/sched_rt_period_us
1 1
set the period (in microseconds) of the reservation.
SCHED_BATCH SCHED_FIFO SCHED_RR SCHED_DEADLINE
Default = 1000000 µs (1 second).

sched_fair sched_rt
/proc/sys/kernel/sched_rt_runtime_us
sched_dl
low priority medium priority high priority
set the budget (in microseconds) to be assigned to the
real‐time activities every period. Default = 950000 µs.
Linux scheduling classes 27

5
07/11/2018

Temporal protection Time representation


Hence, by default, 95% of the CPU is reserved to real‐time In the POSIX standard, time is represented through the
activities and 5% to those scheduled by SCHED_OTHER. following structure, defined in <time.h>:
 This means that a high priority thread can be preempted
struct timespec {
by the system even when scheduled by SCHED_FIFO. time_t tv_sec; /* seconds */
 The percentage reserved to real‐time threads can be long tv_nsec; /* nanoseconds */
modified or disabled (only from root): }

> echo 980000 > /proc/sys/kernel/sched_rt_runtime_us  The type time_t depends on the implementation, but
Sets the budget to 98% of the period. usually it is a 32‐bit integer.
 Unfortunately, the standard library does not provide
> echo ‐1 > /proc/sys/kernel/sched_rt_runtime_us functions to perform operations on time variables, thus
Sets the budget to 100% of the period. it is necessary to define auxiliary functions.

Time copy
This function copies a source time variable ts in a
destination variable pointed by td:

void time_copy(struct timespec *td,


struct timespec ts)
{
td->tv_sec = ts.tv_sec;
td->tv_nsec = ts.tv_nsec;
}

Types of clocks Addition of milliseconds


Linux supports the following clocks: This function adds a value ms expressed in milliseconds to
the time variable pointed by t:
CLOCK_REALTIME: gives the closest
value to the absolute time. However,
it can be discontinuous due to small void time_add_ms(struct timespec *t, int ms)
{
adjustments. t->tv_sec += ms/1000;
t->tv_nsec += (ms%1000)*1000000;
CLOCK_MONOTONIC: represents the
if (t->tv_nsec > 1000000000) {
time elapsed from a given undefined t->tv_nsec -= 1000000000;
instant of time. It is not affected by t->tv_sec += 1;
adjustments, hence it is the best }
solution to measure the time elapsed }
between two events.

6
07/11/2018

Time comparison Example


This function compares two time variables t1 and t2 and struct timespec t; /* absolute time */
returns 0 if they are equal, 1 if t1 > t2, ‐1 if t1 < t2: struct timespec dt; /* time interval */
int delta = 500; /* delay in milliseconds */

int time_cmp( clock_gettime(CLOCK_MONOTONIC, &t);


struct timespec t1, time_add_ms(&t, delta);
struct timespec t2) /* suspend until absolute time t */
{ clock_nanosleep(CLOCK_MONOTONIC,
if (t1.tv_sec > t2.tv_sec) return 1; TIMER_ABSTIME, &t, NULL);
if (t1.tv_sec < t2.tv_sec) return -1;
/* suspend for a relative interval of 500 ms */
if (t1.tv_nsec > t2.tv_nsec) return 1;
dt.tv_sec = 0;
if (t1.tv_nsec < t2.tv_nsec) return -1;
dt.tv_nsec = delta*1000000;
return 0; clock_nanosleep(CLOCK_MONOTONIC, 0, &dt, NULL);
}

Available functions A wrong periodic thread


int clock_getres(clockid_t clk_id, struct timespec *res); void *task(void *arg)
{
If res  NULL, it copies in *res the resolution of the clock struct timespec t;
int period = 100; /* period in milliseconds */
specified by clk_id. Such a resolution depends on the
specific implementation and cannot be changed. while (1) {
/* do useful work */
int clock_gettime(clockid_t clk_id, struct timespec *t); clock_gettime(CLOCK_MONOTONIC, &t);
time_add_ms(&t, period);
It copies in *t the value of the clock specified by clk_id. clock_nanosleep(CLOCK_MONOTONIC,
TIMER_ABSTIME, &t, NULL);
}
int clock_settime(clockid_t clk_id, struct timespec *t); }
Sets the clock specified by clk_id to the value pointed by t. The task has a variable period equal to 100 ms + the
The value is truncated if it is not multiple of the resolution. response time (variable from job to job).

Available functions A periodic thread


int clock_nanosleep(clockid_t clk_id, int flag, void *task(void *arg)
const struct timespec *t, struct timespec *rem); {
struct timespec t;
int period = 100; /* period in milliseconds */
Suspends the execution of the calling thread until the clock
clk_id reaches the time specified by t. clock_gettime(CLOCK_MONOTONIC, &t);
time_add_ms(&t, period);
 If flag = 0, time t is interpreted as relative to the current while (1) {
time; /* do useful work */
 If flag = TIMER_ABSTIME, time t is interpreted as clock_nanosleep(CLOCK_MONOTONIC,
absolute. TIMER_ABSTIME, &t, NULL);
time_add_ms(&t, period);
 If the thread is awaken before the requested time, the }
}
remaining time is stored in rem.

7
07/11/2018

Checking for deadline miss Improving readability


void *task(void *arg) It is worth developing an additional library on top of
{
struct timespec t, now; pthread to simplify the management of periodic tasks:
int period = 100; /* period in milliseconds */

clock_gettime(CLOCK_MONOTONIC, &t);
time_add_ms(&t, period); Application
while (1) {
To be
/* do useful work */ Ptask Library developed
clock_gettime(CLOCK_MONOTONIC, &now);
if (time_cmp(now, t) > 0) exit(-1);
Pthread Library
clock_nanosleep(CLOCK_MONOTONIC,
TIMER_ABSTIME, &t, NULL);
time_add_ms(&t, period);
} Linux primitives
}

Real-Time parameters Task abstraction


They can be stored in a dedicated structure: function index period rel.dl prio
tp
struct task_par {
int arg; /* task argument */
long wcet; /* in microseconds */ TASK
int period; /* in milliseconds */
int deadline; /* relative (ms) */
int priority; /* in [0,99] */ attr
int dmiss; /* no. of misses */ same for
scheduler
struct timespec at; /* next activ. time */ all threads
struct timespec dl; /* abs. deadline */
};
&tid &attr function &tp
 Such a structure must be initialized before calling the
thread_create and passed as a thread argument. THREAD
 A structure for each thread is required.

Example: main Example: task creation


struct sched_param mypar; int idx = 1; /* task index */
struct task_par tp[NT]; arrays of variables
pthread_attr_t att[NT]; int period = 20; /* task period */
pthread_t tid[NT]; for all the threads int dline = 15; /* task relative deadline */
for (i=0; i<NT; i++) {  If known, the WCET can be int prio = 30; /* task priority */
tp[i].arg = i; used for the guarantee test.
tp[i].period = 100; task_create(mytask, idx, period, dline, prio);
tp[i].deadline = 80;  The absolute deadline is set
tp[i].priority = 20; online as soon as the activation
tp[i].dmiss = 0; time is known. In this solution, the task index is decided by the user.
pthread_attr_init(&att[i]);
pthread_attr_setinheritsched(&att[i], PTHREAD_EXPLICIT_SCHED); The user must guarantee that:
pthread_attr_setschedpolicy(&att[i], SCHED_FIFO);
mypar.sched_priority = tp[i].priority;  each task is assigned a different index;
pthread_attr_setschedparam(&att[i], &mypar);  the index does not exceed the size of the tp array.
pthread_create(&tid[i], &att[i], task, &tp[i]);
}

8
07/11/2018

Example: main Scheme of a periodic task


struct sched_param mypar; void *task(void *arg)
struct task_par tp[NT]; {
<local state variables>
for (i=0; i<10; i++) { int ti;
task_create(ball, i, 50, 40, 30);
} ti = get_task_index(arg);
task_create(control, 10, 20, 20, 40); set_activation(ti);
task_create(graphics, 10, 25, 25, 20);
task_create(keyboard, 10, 30, 30, 10); while (1) {
<task body>
if (deadline_miss(ti)) <do action>;
wait_for_activation(ti);
}
}

Task creation (1) get_task_index()


int task_create( int get_task_index(void* arg)
void*(*task)(void *), {
int i, struct task_par *tp;
int period,
int drel, tp = (struct task_par *)arg;
int prio) return tp->arg;
{ }
pthread_attr_t myatt;
struct sched_param mypar; Retrieves the task index stored in tp->arg.
int tret;
NOTE: Since the thread argument is passed as a pointer to
tp[i].arg = i;
tp[i].period = period; void, such a pointer must first be converted to a
tp[i].deadline = drel; pointer to task_par.
tp[i].priority = prio;
tp[i].dmiss = 0;

Task creation (2) set_activation()


pthread_attr_init(&myatt); void set_activation(int i)
pthread_attr_setinheritsched(&myatt, {
PTHREAD_EXPLICIT_SCHED); struct timespec t;
pthread_attr_setschedpolicy(&myatt, SCHED_RR); clock_gettime(CLOCK_MONOTONIC, &t);
mypar.sched_priority = tp[i].priority; time_copy(&(tp[i].at), t);
time_copy(&(tp[i].dl), t);
pthread_attr_setschedparam(&myatt, &mypar);
time_add_ms(&(tp[i].at), tp[i].period);
tret = pthread_create(&tid[i], &myatt, task, time_add_ms(&(tp[i].dl), tp[i].deadline);
(void*)(&tp[i])); }
return tret;
} Reads the current time and computes the next activation
time and the absolute deadline of the task.

NOTE: the timer is not set to interrupt.

9
07/11/2018

deadline_miss() Thread Joining


int deadline_miss(int i) Allows waiting the termination of 
{ Main thread one or more threads
struct timespec now; thread 1 thread 2
clock_gettime(CLOCK_MONOTONIC, &now); pthread_create()
pthread_create()
if (time_cmp(now, tp[i].dl) > 0) {
tp[i].dmiss++;
return 1;
}
return 0; pthread_exit()
}
pthread_join()
If the thread is still in execution when re‐activated, it pthread_exit()
pthread_join()
increments the value of dmiss and returns 1, otherwise
returns 0.

wait_for_activation() Semaphores
void wait_for_activation(int i) General semaphores are not part of the pthread library,
{ since are defined in the POSIX standard.
clock_nanosleep(CLOCK_MONOTONIC,
TIMER_ABSTIME, &(tp[i].at), NULL);
 To use them, you need to include <semaphore.h>.
time_add_ms(&(tp[i].at), tp[i].period);  A semaphore is a variable of type sem_t.
time_add_ms(&(tp[i].dl), tp[i].period);
} Main functions:
 sem_init: initializes a semaphore.
Suspends the calling thread until the next activation and,
 sem_destroy: reclaims the memory needed for a
when awaken, updates activation time and deadline. semaphore that is not used.
NOTE: Even though the thread calls time_add_ms() after  sem_wait: wait on a semaphore.
the wake‐up time, the computation is correct.  sem_post: signal on a semaphore.
 sem_getvalue: returns the value of a semaphore.

Thread synchronization Initializing semaphores


Linux provides the following mechanisms for synchronizing int sem_init (sem_t *sem, int pshared, unsigned int v);
threads:
Joining allows waiting the termination of one or more Initializes a semaphore:
threads.
sem pointer to a semaphore;
Semaphore allows mutual exclusion and synchronization.
pshared if 0, specifies that sem is shared among threads
Mutex allows safer and more advanced mutual (hence it must be declared as global); if  0,
exclusion. specifies that sem is shared among processes.
Condition allows waiting on conditional expressions. v initial value of the semaphore;
Barrier allows more threads to suspend and restart
until they all reach a certain point. Returns 0 in case of success, ‐1 in case of error.

10
07/11/2018

Using semaphores Synchronizing on an event


int sem_wait (sem_t *sem);
sem_t event; Initial value = 0
sem_init(&event, 0, 0);
If the current value of the semaphore is 0, it blocks the
calling thread until a sem_post is executed, otherwise it thread 1 thread 2
decreases the semaphore and exits.

int sem_post (sem_t *sem);


sem_post(&event)

If there are threads blocked on the semaphore, it awakes sem_wait(&event)

the one with the highest priority, otherwise it increments


the semaphore and exits.
Both functions return 0 on success and ‐1 on error.

Using semaphores Mutual exclusion


struct point {
int sem_destroy (sem_t *sem); Global data structure shared
int x;
int y; by threads in mutual exclusion.
It reclaims the memory used for the semaphore. } p;
void *writer();
void *reader();
sem_t s; Semaphore definition
int sem_getvalue (sem_t *sem, int *pval); int main()
{
It reads the value of the semaphore and writes it in the pthread_t tid1, tid2;
variable pointed by pval. sem_init(&s, 0, 1); Semaphore initialization
pthread_create(&tid1, NULL, writer, NULL);
pthread_create(&tid2, NULL, reader, NULL);
Both functions return 0 on success and ‐1 on error.
/* do some useful work */
}

Example - semaphores Mutual exclusion


#include <semaphore.h> void *writer()
{
sem_t sem1, sem2, sem3; /* defines 3 semaphores */
sem_wait(&s);
int main() p.x++;
{ p.y++;
sem_post(&s);
/* initializes sem1 for synchronization */ }
sem_init(&sem1, 0, 0);

/* initializes sem2 for mutual exclusion */ void *reader()


sem_init(&sem2, 0, 1); {
sem_wait(&s);
/* initializes sem3 for simultaneous access */
/* of max 4 threads to a 4-units resource */ printf("(%d,%d)\n", p.x, p.y);
sem_init(&sem3, 0, 4); sem_post(&s);
}

11
07/11/2018

Mutex semaphores Example - Mutex


A MUTEX is a special type of binary semaphore with some void *writer()
restrictions aimed at containing programming errors: {
 it can only be used for mutual exclusion, not for pthread_mutex_lock(&mux);
synchronization; p.x++;
 a mutex locked by a thread can be unlocked only by p.y++;
the same thread. pthread_mutex_unlock(&mux);
}
Main functions:
 pthread_mutex_init: initializes a mutex. void *reader()
{
 pthread_mutex_destroy: reclaims the memory of a pthread_mutex_lock(&mux);
mutex that is no more used. printf("(%d,%d)\n", p.x, p.y);
 pthread_mutex_lock: wait on a mutex. pthread_mutex_unlock(&mux);
}
 pthread_mutex_unlock: signal on a mutex.

Initializing a mutex Mutex protocols


A mutex must be declared and initialized before using it. Three protocols can be defined on a mutex:
There are 3 ways for initializing a mutex:
pthread_mutex_t mux; /* define a mutex */ PTHREAD_PRIO_NONE
pthread_mutexattr_t matt; /* define attributes */
No protocol (classic semaphore, default value).
/* Way 1: direct initialization */
pthread_mutex_t mux = PTHREAD_MUTEX_INITIALIZER; PTHREAD_PRIO_INHERIT
/* Way 2: initialization with default values */
Priority Inheritance protocol.
pthread_mutex_init(&mux, NULL);
PTHREAD_PRIO_PROTECT
/* Way 3: initialization with attributes */ Immediate Priority Ceiling protocol
pthread_mutexattr_init(&matt);
pthread_mutex_init(&mux, &matt); (also known as Highest Locker Priority).
In the reported example, the three ways are equivalent and NOTE: these are not supported by POSIX semaphores.
initialize the mutex with the default values.

Example - Mutex Priority Inheritance


struct point {
Global data structure shared #define _GNU_SOURCE
int x;
int y; by threads in mutual exclusion. pthread_mutex_t mux1, mux2; /* define 2 mutexes */
} p; pthread_mutexattr_t matt; /* define mutex attrib. */
Definition and initialization of
void *writer(); a mutex semaphore. pthread_mutexattr_init(&matt); /* initialize attributes */
void *reader(); pthread_mutexattr_setprotocol(&matt, PTHREAD_PRIO_INHERIT);
pthread_mutex_t mux = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_init(&mux1, &matt);
int main()
pthread_mutex_init(&mux2, &matt);
{
pthread_t tid1, tid2; pthread_mutexattr_destroy(&matt); /* destroy attributes */
pthread_create(&tid1, NULL, writer, NULL);
pthread_create(&tid2, NULL, reader, NULL);  You need to insert: #define _GNU_SOURCE
pthread_join(tid1, NULL);  Each mutex can use a different protocol.
pthread_join(tid2, NULL);
return 0;  The same variable matt can be used to initialize
} different semaphores.

12
07/11/2018

Immediate Priority Ceiling Condition variables


#define _GNU_SOURCE pthread_mutex_t mux; /* define a mutex */
pthread_cond_t cv; /* define a cond. variable */
pthread_mutex_t mux1, mux2; /* define 2 mutexes */ int count = 0; /* variable to be checked */
pthread_mutexattr_t matt; /* define mutex attrib. */
int main()
pthread_mutexattr_init(&matt);
{
pthread_mutexattr_setprotocol(&matt, PTHREAD_PRIO_PROTECT);
/* initialize variables with default attributes */
pthread_mutexattr_setprioceiling(&matt, 10); pthread_mutex_init(&mux, NULL);
pthread_mutex_init(&mux1, &matt); pthread_cond_init(&cv, NULL);
pthread_mutexattr_setprioceiling(&matt, 15);
/* do some useful work */
pthread_mutex_init(&mux2, &matt);

pthread_mutexattr_destroy(&matt); /* reclaim memory */


pthread_mutex_destroy(&mux);
 The ceiling of a mutex must be set equal to the highest }
pthread_cond_destroy(&cv);

priority among the threads using it.

Condition variables Waiting thread


Such a mechanism allows a thread to wait a predefined void *watch_count()
value of a shared variable: {
/* wait until count reaches the THRESHOLD */
Waiting thread Signaling thread
pthread_mutex_lock(&mux);
The loop is necessary  while (count < THRESHOLD) {
because the wake up can be 
pthread_cond_wait(&cv, &mux);
caused by other reasons
x = x + 1 }

Y pthread_mutex_unlock(&mux);
x != 8 sleep Y If more threads are waiting, 
x == 8
it is possible to wake up a  /* do some useful work */
N
N single one or  all together
pthread_exit(NULL);
}

Condition variables Signaling thread


They are always used together with a mutex: void *inc_count()
{
Before blocking, the mutex must be unlocked
atomically and re‐locked at wake up. /* do some useful work */
pthread_mutex_lock(&mux);
Waiting thread Signaling thread
count++;
if (count == THRESHOLD) {
pthread_mutex_unlock(&mux);
lock(mux);
x  = x + 1; pthread_cond_signal(&cv);
lock(mux); }
if (x == 8) {
while (x != 8) { else pthread_mutex_unlock(&mux);
unlock(mux);
sleep(cond_var, mux);
signal(cond_var);
}
} /* do some useful work */
unlock(mux);
else unlock(mux);
pthread_exit(NULL);
}

13
07/11/2018

Brodcasting Barrier
void *inc_count() #define NT 10
{ pthread_barrier_t barr;
A barrier must
be declared first
/* do some useful work */ All waiting threads can be  int main()
pthread_mutex_lock(&mux); awaken by a broadcast { and initialized with the number of
pthread_t tid[NT];
count++; threads that need to synchronize
int i;
if (count == THRESHOLD) {
pthread_mutex_unlock(&mux); /* initialize barrier */
pthread_cond_broadcast(&cv); pthread_barrier_init(&barr, NULL, NT);
}
else pthread_mutex_unlock(&mux); for (i=0; i<NT; i++)
pthread_create(&tid[i], NULL, &task, (void*)i));
/* do some useful work */
/* do some useful work */
pthread_exit(NULL);
} return 0;
}

Notes Sync at barrier


The synchronization at a barrier occurs by the function
int pthread_cond_signal (pthread_cond_t *cond);
pthread_barrier_wait:
int pthread_cond_broadcast (pthread_cond_t *cond);
int pthread_barrier_wait (pthread_barrier_t *barr);
 The signal awakes only one of the threads blocked on
the condition variable. The awaken thread depends on It blocks the calling thread until a number of threads equal
the scheduler (under the real‐time policies, it is the one to the one specified in pthread_barrier_init executed the
with the highest priority). pthread_barrier_wait.
In case of success, the function returns the value
 The broadcast awakes all the threads blocked on the PTHREAD_BARRIER_SERIAL_THREAD to an unspecified
condition variable. thread and 0 to the other threads. Hence, the barrier is
reset to the state defined in the last pthread_barrier_init.
 Both functions have no effect if no threads are blocked.
In case of failure, an error code is returned.

Barrier Thread sync at barrier


Each thread blocks until all execute the barrier_wait: void *task(void *arg)
{
int rc;
thread 1 thread 2 thread 3
/* do some useful work */
All wait the 
/* synchronization point */
arrival of the  rc = pthread_barrier_wait(&barr);
slowest  thread if ((rc != 0) &&
(rc != PTHREAD_BARRIER_SERIAL_THREAD)) {
barrier_wait() printf("Could not wait on barrier\n");
barrier_wait()
exit(-1);
}
barrier_wait()
/* do some useful work */
BARRIER
pthread_exit(NULL);
}

14

You might also like