Table of Contents

Exercises about concurrent programming (Pseudo-code)

In this section some exercises about concurrent programming will be presented. The solutions of the exercises are provided using a pseudo-code similar to the C programming language. The functions P() and V() have the same behavior of the POSIX functions sem_wait() and sem_post(), respectivelly. Assume that it is possible to define and to initialize a semaphore with a pseudo-statement like: sem_t <initial_value>;. The variables with name m (m, m1, m2, …) are semaphore used as mutex.

It is assumed, for all the exercises, that the threads used for the implementation of the concurrent program have already been created. Indeed, the purpose of the exercises is to realize only the functions that allow the synchronization and the communication between these concurrent threads. For a complete running program, to the provided solution, the function pthread_create() should be added, in order to start the execution of the threads.

Exercise 1 (Semaphore with priority)

An undefined number of threads Ti can call the functions int p(int prio, int value_p) and int v(int value_v). These functions must simulate (by using classical semaphores and under the hypothesis that the threads are queued by these semaphores in a FIFO order), the behavior of semaphores that queue threads in an order depending on the value of the variable prio, that is an argument of the function p() (0 < = prio < = 9, prio=9 is the maximum priority).
The behavior of the two functions is:

int p(int prio, int value_p): when the value of the semaphore is a number greater than 0, the function decreases of one the value and returns 0. In the case the value of the semaphore is 0, the function is blocking. When unblocked by the calling of the v() function, the function p() must return to the caller the value value_v, previously passed as argument to the function v().

int v(int value_v): if executed when no thread is blocked on p(), it increments of one the value of the semaphore and returns 0. In the case at least a thread is blocked on p(), the function must unblock the blocked thread with higher priority and it must return the value value_p, passed as argument to the function p(). In the case of more than two threads with the same priority are blocked by the semaphore, the following v() will unblock one of the threads.

Example 2 (Message broadcast)

An undefined number of threads Ci can call the function int bConsume() and only one thread P can call the function void bProduce(int value).

The behavior of the two functions is:

int bConsume() is blocking while waiting that the thread P calls the function bProduce(int value). The function bProduce(int value), when called, will unblock all the thread blocked on int bConsume(). The function int bConsume() must return to any unblocked thread the value value passed as an argument to the function bProduce(int value).

bProduce(int value), in the case some threads are blocked by int bConsume(), it must unblock such threads and it must pass to them the value value. In the case no thread is blocked by the function int bConsume(), it must block the process P waiting that a thread executes the function int bConsume(). After unblocking the thread P, the function bProduce(int value) must pass the value value to the function int bConsume() that, because in this case it does not block any thread, it will return immediately the value value to the calling thread.

Realize the functions int bConsume() and bProduce(int value).

Example:

C1 calls bConsume() and it blocks on it

C2 calls bConsume() and it blocks on it

P calls bProduce(21) which unblocks the threads C1 and C2 that are blocked on bConsume(). The function bConsume() returns to both threads the value 21

P calls bProduce(12) and it blocks on it

C2 calls bConsume() that unblocks the threads P and returns to the thread C2 the value 12

Example 3 (Multiplication between threads)

An undefined number of threads Ti can call the function int val(int v) and only one thread M can call the function void mul(int m).

The behavior of the two functions is:

int val(int v) blocks the calling thread waiting for the thread M calls the function void mul(int m). When the function void mul(int m) is called by the process M, it must unblock all the threads T blocked until that moment by int val(int v). To each unblocked thread T, the function int val(int v) must return the value v multiplied by the value m.

void mul(int m), besides the behavior described in the preceding point, in the case no thread is blocked by int val(int v), it must block the calling process waiting for a thread of type T that calls the function int val(int v). When such an event happens, the thread M must be unblocked, and the function int val(int v), called by the thread T, must immediately return the value v*m.

Realize the functions int val(int v) and void mul(int m).

Example:

T1 calls val(3) and it blocks on it

T2 calls val(5) and it blocks on it

M calls mul(2) which unblocks the threads T1 and T2 blocked on val(). The two functions val() will return to the calling threads, T1 and T2, the values 3*2=6 and 5*2=10, respectively

M calls mul(3) and it blocks on it

T2 calls val(4) which unblocks the thread M and it will return immediately to the thread T2 the value 4*3=12

Example 4

An undefined number of threads Ti can call the functions int wait(int w_prio, int max_proc) and int unblock(int s_prio, int value). The parameters w_prio and s_prio can assume values between 0 and 9.

The behavior of the two functions is:

int wait(int w_prio, int max_proc) is blocking if the total number of threads blocked until it calling is less than max_proc, alternatively the function wait returns the value -1. If the threads blocked by wait are unblocked by the function unblock, the function wait must return the value value (passed as an argument of the function unblock).

int unblock(int s_prio, int value) unblocks all the threads blocked by the function wait that have the value w_prio equal to s_prio. The function wait must return to all the unblocked thread the value value. The function unblock returns the number of unblocked thread (or -1 in the case no thread is unblocked).

Pay particular attention to the fact that the value value is returned correctly to all the unblocked threads and that the threads Ti, that execute the function wait after the function unblock, are properly unblocked by the function wait.