/* 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...]