Producer/consumer race condition example: /* "buffer" stores BUFFER_SIZE items "count" is number of used slots. a variable that lives in memory "out" is next empty buffer slot to fill (if any) "in" is oldest filled slot to consume (if any) */ void producer (void *ignored) { for (;;) { /* next line produces an item and puts it in nextProduced */ nextProduced = means_of_production(); while (count == BUFFER_SIZE) ; // do nothing buffer [in] = nextProduced; in = (in + 1) % BUFFER_SIZE; count++; } } void consumer (void *ignored) { for (;;) { while (count == 0) ; // do nothing nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; count−−; /* next line abstractly consumes the item */ consume_item(nextConsumed); } } /* what count++ probably compiles to: reg1 <−− count # load reg1 <−− reg1 + 1 # increment register count <−− reg1 # store what count−− could compile to: reg2 <−− count # load reg2 <−− reg2 − 1 # decrement register count <−− reg2 # store */ What happens if we get the following interleaving? reg1 <−− count reg1 <−− reg1 + 1 reg2 <−− count reg2 <−− reg2 − 1 count <−− reg1 count <−− reg2 ================================================================ Removing race conditions ================================================================ The examples above depict cases of race conditions. This part demonstrates the use of concurrency primitives (mutexes, etc.) to protect critical sections and eliminate race conditions. 1. Protecting the linked list...... Mutex list_mutex; insert(int data) { List_elem* l = new List_elem; l−>data = data; acquire(&list_mutex); l−>next = head; head = l; release(&list_mutex) 2. Produce/consume revisited (also known as bounded buffer) Mutex mutex; void producer (void *ignored) { for (;;) { /* next line produces an item and puts it in nextProduced */ nextProduced = means_of_production(); acquire(&mutex); while (count == BUFFER_SIZE) { release(&mutex); yield(); /* or schedule() */ acquire(&mutex); } buffer [in] = nextProduced; in = (in + 1) % BUFFER_SIZE; count++; release(&mutex); } } void consumer (void *ignored) { for (;;) { acquire(&mutex); while (count == 0) { release(&mutex); yield(); /* or schedule() */ acquire(&mutex); } nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; count−−; release(&mutex); /* next line abstractly consumes the item */ consume_item(nextConsumed); } } ===============================================================