Locks: protect critical section Coarse-grained locking vs. fine-grained locking Deadlock (avoid using lock ordering) void transfer(int acc_x, int acc_y, int amt) { lock(m); acc_x -= amt; acc_y += amt; unlock(m); } int sum(int acc_x, int acc_y) { int s; lock(m); s = acc_x + acc_y; unlock(m); } Fine-grained locking is more error-prone and has danger of deadlock (whenever a thread needs to hold >=2 locks) void transfer(int acc_x, int acc_y, int amt) { lock(acc_x_m); acc_x -= amt; unlock(acc_x_m); lock(acc_y_m); acc_y += amt; unlock(acc_y_m); } transfer sum acc_x -= amt read acc_x read acc_y acc_y += amt --> sum() can return a sum of account balances that's less than the actual total Conditional Variables: scheduling shared resources //example implement a channel connecting senders and receivers typedef struct { pthread_mutex_t m; pthread_cond_t c; int *val; //can buffer one value }channel; channel chan; .. int send_v = 1; void * sender_run(void *) { chan_send(&send_v); } void * receiver_run(void *) { int *v = chan_recv(&i); printf("recv %d\n", *v); } for (int i = 0; i < 5; i++) pthread_create(.., sender_run, NULL); for (int i = 0; i < 5; i++) pthread_create(.., receiver_run, NULL); void chan_send(int *v) { pthread_mutex_lock(&chan.m); while (chan.val != NULL) pthread_cond_wait(&chan.c, &chan.m); chan.val = v; pthread_mutex_unlock(&chan.m); } int * chan_recv() { //wait till c->val becomes not NULL pthread_mutex_lock(&chan.m); int *v = chan.val; chan.val = NULL; pthread_cond_signal(&chan.c); pthread_mutex_unlock(&chan.m); return v; } Next how to get rid of the second wait in our channel example? void chan_send(int *v) { ... c.val = v; pthread_cond_broadcast(&chan.c); pthread_mutex_unlock(&chan.m); } int * chan_recv() { pthread_mutex_lock(&chan.m); while (chan.v == NULL) pthread_cond_wait(&chan.c, &chan.m); ... pthread_cond_broadcast(&chan.c); phtread_mutex_unlock(&chan.m); return v; } This code is correct but has spurious wakeups. A better version: void chan_send(int *v) { pthread_mutex_lock(&m); while (c->val != NULL) pthread_cond_wait(&c_null, &m); c->val = v; pthread_signal(&c_notnull); pthread_cond_unlock(&m); } int * chan_recv() { pthread_mutex_lock(&m); while (c->val == NULL) pthread_cond_wait(&c_notnull,&m); int *v = c->val; c->val = NULL; pthread_cond_signal(&c_null); pthread_mutex_unlock(&m); return v; } Let's do another example: barrier void * thread_run(void *arg) { printf("one\n"); barrier_wait(&b1); printf("two\n"); barrier_wait(&b2); printf("three\n"); } ... for (int i = 0; i < 10; i++) pthread_create(th[i], thread_run, NULL); typedef struct { pthread_mutex_t m; pthread_cond_t all_completed; int n; }barrier_t; void barrier_init(barrier_t *b, int n) { b->n = n; pthread_mutex_init(&b->m, NULL); pthread_cond_init(&b->all_completed, NULL); } void barrier_wait(barrier_t *b) { pthread_mutex_lock(&b->m); b->n--; if (b->n == 0) { pthread_cond_broadcast(&b->all_completed); }else { while (b->n > 0) pthread_cond_wait(&b->all_completed, &b->m); } pthread_mutex_unlock(&b->m); }