/* * Synchronization via the monitor pattern. * Unbounded buffer example. */ #include #include #include #include #include #include #include "list.h" // a header-only library #include "monitor.h" struct item { int item; struct list_elem elem; }; struct monitor monitor; struct list queue; // accessed only inside the monitor /* Produce one item and place it into the queue. */ void produce(int _item) { struct item *item = malloc(sizeof *item); assert (item != NULL); // ignore memory exhaustion item->item = _item; monitor_enter(&monitor); // Enter monitor list_push_front(&queue, &item->elem); bool rc = monitor_signal(&monitor); // wake up consumer (if any) assert (rc == true); monitor_exit(&monitor); // Leave monitor } /* Consume one item from the queue and return it. */ int consume() { monitor_enter(&monitor); // Enter monitor while (list_empty(&queue)) assert(monitor_wait(&monitor) == true); struct list_elem *e = list_pop_back(&queue); struct item *item = list_entry(e, struct item, elem); int _item = item->item; free (item); monitor_exit(&monitor); // Leave monitor return _item; } /* A buggy attempt at consuming an item - waits * without being inside the monitor. * This must be detected and fail. */ void buggy_consume() { assert(monitor_wait(&monitor) == false); } /* A buggy attempt at producing an item - signals * without being inside the monitor. * This must be detected and fail. */ void buggy_produce() { assert(monitor_signal(&monitor) == false); } /* Produce a number of items. * Occasionally invoke buggy_produce */ static void * producer(void *_items_to_produce) { int n = *(int *)_items_to_produce; for (int i = 0; i < n; ) { if (rand() % 5 == 0) { buggy_produce(); } else { produce(i); i++; } } return NULL; } /* Consume a number of items, return their sum. */ static void * consumer(void *_items_to_consume) { int n = *(int *)_items_to_consume; uintptr_t sum = 0; for (int i = 0; i < n; ) { if (rand() % 5 == 0) { int item = consume(); sum += item; i++; } else buggy_consume(); } return (void *) sum; } int main() { #define N 4 int items [N] = { 30000, 20000, 40000, 10000 }; void * (*func [N])(void*) = { consumer, consumer, producer, producer }; srand(getpid()); monitor_init(&monitor); list_init(&queue); pthread_t t[N]; for (int i = 0; i < N; i++) pthread_create(&t[i], NULL, func[i], items + i); long sum = 0; for (int i = 0; i < N; i++) { uintptr_t val; pthread_join(t[i], (void **) &val); sum += val; } printf("sum %ld\n", sum); assert (sum == items[2] * (items[2]-1) / 2 + items[3] * (items[3]-1) / 2); return 0; }