Added a multi producer single consumer queue
[sandbox] / vm / vm.c
1 #include <stdbool.h>
2 #include <stdlib.h>
3
4 struct Queue;
5 typedef struct Queue Queue;
6 struct QueueNode;
7 typedef struct QueueNode QueueNode;
8
9 void initializeQueue(Queue* queue);
10 void enqueue(Queue* queue, void* item);
11 void* dequeue(Queue* queue);
12 void freeQueue(Queue* queue, void (*itemFreer)(void*));
13
14 struct Queue
15 {
16   // This implements a multiple-producer, single-consumer lockless concurrent
17   // queue.
18   //
19   // After initialization, `head` and `tail` always points to a valid memory
20   // location--they are never `NULL`. To achieve this we initialize `head` and
21   // `tail` with a dummy node. This has two negative implications:
22   //
23   // 1. Unintuitively, `head->item` actually represents the item most recently
24   //    dequeued. In fact, `head->item` may have been freed after it was
25   //    dequeued, so we should never dereference it.
26   // 2. At initialization, no item has been dequeued yet, so we must allocate
27   //    an extra `QueueNode` at initialization which will never be used to
28   //    store an item.
29   //
30   // However, this extra memory allocation at initialization means that we
31   // never have to check whether `head` or `tail` are `NULL`, or initialize
32   // these variables concurrently, which are expensive operations.
33   QueueNode* head;
34   QueueNode* tail;
35 };
36
37 struct QueueNode
38 {
39   // This implements a singly-linked list.
40   void* item;
41   QueueNode* next;
42 };
43
44 void initializeQueue(Queue* queue)
45 {
46   // Initialize the queue with a node that fakes the previously dequeued item.
47   // We don't have to initialize `dummyNode->item` because it will never be
48   // used.
49   QueueNode* dummyNode = malloc(sizeof(QueueNode));
50   dummyNode->next = NULL;
51   queue->head = dummyNode;
52   queue->tail = dummyNode;
53 }
54
55 bool isEmpty(Queue* queue)
56 {
57   // In a normal singly-linked list, the list is empty when the `head` is
58   // `NULL`, but here the queue is empty when the `head` is the same as the
59   // `tail`.
60   return queue->head == queue->tail;
61 }
62
63 void enqueue(Queue* queue, void* item)
64 {
65   // Create the node that will be the new tail.
66   QueueNode* node = malloc(sizeof(QueueNode));
67   node->item = item;
68   node->next = NULL;
69
70   // Append the node to the list. Note that this is blocked as long as
71   // `queue->tail` is not actually pointing to the last node in the list
72   // because then `queue->tail->next` will not be `NULL`. This situation
73   // occurs when another thread has called `enqueue` and appended a node to
74   // the list, but hasn't yet updated `queue->tail`.
75   while(!__sync_bool_compare_and_swap(&(queue->tail->next), NULL, node));
76
77   // Move `queue->tail` forward until it points to the end of the list.
78   // Other threads attempting to eqneue will be unable to do so until this
79   // is complete. Additionally, other threads attempting to dequeue will be
80   // blocked until `queue->tail` has advanced by at least one node.
81   //
82   // We don't have to worry that another thread might have appended more
83   // nodes; advancing `queue->tail` to the end of the linked list is always
84   // the correct thing to do.
85   QueueNode* tail;
86   while((tail = queue->tail)->next != NULL)
87   {
88     __sync_val_compare_and_swap(&(queue->tail), tail, tail->next);
89   }
90 }
91
92 void* dequeue(Queue* queue)
93 {
94   if(isEmpty(queue))
95   {
96     return NULL; // TODO Provide ability to return a defined default.
97   }
98
99   QueueNode* oldHead = queue->head;
100   queue->head = oldHead->next;
101   free(oldHead);
102
103   return queue->head->item;
104 }
105
106 void freeQueue(Queue* queue, void (*itemFreer)(void*))
107 {
108   QueueNode* current = queue->head;
109   free(queue);
110
111   while(current != NULL)
112   {
113     itemFreer(current->item);
114     QueueNode* temp = current->next;
115     free(current);
116     current = temp;
117   }
118 }
119
120 int main(int argc, char** argv)
121 {
122   return EXIT_SUCCESS;
123 }