/* Consommateur */ pthread_mutex_lock(&lock); while (!expression) pthread_cond_wait(&cond, &lock); do_thing(); pthread_mutex_unlock(&lock);/* Producteur, Signaleur */ expression = TRUE; pthread_cond_signal(&cond);
On reprend l’exemple du producteur/consommateur vu en cours :
Identifiez la synchronisation nécessaire entre un processus léger en cours de calcul d’une valeur et un processus légers ayant besoin de cette valeur. Proposez une implémentation de cette synchronisation.#define INCONNUE -1 #define ENCOURS -2 static int fibtab [];
L’interface de cette bibliothèque sera la suivante :
typedef struct barrier_s barrier_t; barrier_t *barrier_init(unsigned n_clients); void barrier_destroy(barrier_t *barrier); void barrier_wait(barrier_t *barrier);
La fonction barrier_init() alloue et initialise une barrière qui sera utilisée par n_clients.
La fonction barrier_destroy() demande la destruction de la barrière. Aucun thread ne doit attendre sur la barrière lors de la destruction.
La fonction barrier_wait() est appelée par un thread pour rentrer dans la barrière. Quand n_clients threads ont atteint la barrière, ils sont tous libérés.
On considère maintenant l’utilisation suivante de la bibliothèque :
#define N ...
barrier_t b;
static void *
foo(void *dummy)
{
do_thing1();
barrier_wait(&b);
do_thing2();
barrier_wait(&b);
pthread_exit(0);
}
int
main(void)
{
int i;
pthread_t dummy_tid;
b = barrier_init(N);
for (i=1; i<N; i++)
pthread_create(&dummy_tid, NULL,
foo, NULL);
foo();
}
Elle est appelée par un thread qui atteint la barrière. Quand n_clients threads ont atteint la barrière, ils sont tous libérés. Le résultat retourné est la somme de tous les increment passés par les threads lors de leur appel à barrier_wait.int barrier_wait(barrier_t *barrier, int increment);
barrier_t *barrier_init(unsigned n_slaves); void barrier_destroy(barrier_t *barrier); int barrier_master(barrier_t *barrier); int barrier_release(barrier_t *barrier); int barrier_slave(barrier_t *barrier, int increment);
La fonction barrier_init() est appelée par le thread maître pour allouer et initialiser une barrière qui sera utilisée avec n_slaves threads esclaves.
La fonction barrer_destroy() est appelée par le thread maître pour détruire la barrière.
La fonction barrier_master() est appelée par le thread maître pour attendre que les n_slaves threads esclaves aient atteint la barrière. Quand c’est le cas, barrier_master() retourne la somme des incréments donnés par l’ensemble des esclaves.
La fonction barrier_release() est appelée par le maître pour libérer l’ensemble des esclaves qui attendent sur la barrière.
La fonction barrier_slave() est appelée par un thread esclave qui atteint la barrière. La valeur increment est accumulée pour être retournée (barrier_master()) au maître. barrier_slave() retourne après que le maître ait appelé barrier_release(). La valeur retournée est la somme des increment de l’ensemble des esclaves (i.e. la même valeur que celle retournée au maître par barrier_master()).
typedef struct _rec_mutex_s rec_mutex_t;
struct _rec_mutex_s { ... };
#define REC_MUTEX_INITIALIZER ...
int rec_mutex_init(rec_mutex_t *rm);
int rec_mutex_destroy(rec_mutex_t *rm);
int rec_mutex_lock(rec_mutex_t *rm);
int rec_mutex_unlock(rec_mutex_t *rm);
int rec_mutex_trylock(rec_mutex_t *rm);
[fin provisoire du sujet...]