| [8265082] | 1 | /* Non-Blocking Concurrent Queue Algorithm
|
|---|
| 2 | * from Michael and Scott
|
|---|
| 3 | * https://www.cs.rochester.edu/research/synchronization/pseudocode/queues.html.
|
|---|
| 4 | * Originally from "Simple, Fast, and
|
|---|
| 5 | * Practical Non-Blocking and Blocking
|
|---|
| 6 | * Concurrent Queue Algorithms", PODC96.
|
|---|
| 7 | *
|
|---|
| 8 | * The free in the algorithm (setFree method
|
|---|
| 9 | * in Dequeue in this code) is meant to
|
|---|
| 10 | * represent a function putting the node back
|
|---|
| 11 | * on to a locally-maintained special-use free
|
|---|
| 12 | * list and not the partner to malloc.
|
|---|
| 13 | * http://blog.shealevy.com/2015/04/23/use-after-free-bug-in-maged-m-michael-and-michael-l-scotts-non-blocking-concurrent-queue-algorithm/#up1
|
|---|
| 14 | */
|
|---|
| 15 | #include <civlc.cvh>
|
|---|
| 16 | #include <stdio.h>
|
|---|
| 17 | #include <stdbool.h>
|
|---|
| 18 | #include <stdlib.h>
|
|---|
| 19 | #include <assert.h>
|
|---|
| 20 |
|
|---|
| 21 | typedef struct pointer_t pointer_t;
|
|---|
| 22 | typedef struct queue_t queue_t;
|
|---|
| 23 | typedef struct node_t node_t;
|
|---|
| [b60c60f] | 24 | typedef struct free_t free_t;
|
|---|
| [8265082] | 25 |
|
|---|
| 26 | struct pointer_t {
|
|---|
| 27 | node_t* ptr;
|
|---|
| 28 | int count;
|
|---|
| 29 | };
|
|---|
| 30 |
|
|---|
| 31 | struct node_t {
|
|---|
| 32 | int value;
|
|---|
| 33 | pointer_t next;
|
|---|
| 34 | };
|
|---|
| 35 |
|
|---|
| 36 | struct queue_t {
|
|---|
| 37 | pointer_t Head;
|
|---|
| 38 | pointer_t Tail;
|
|---|
| 39 | };
|
|---|
| 40 |
|
|---|
| [b60c60f] | 41 | /* Structure for linked list consisting of nodes
|
|---|
| 42 | * that we want to free eventually */
|
|---|
| 43 | struct free_t {
|
|---|
| [8265082] | 44 | node_t *node;
|
|---|
| [b60c60f] | 45 | free_t *next;
|
|---|
| [8265082] | 46 | };
|
|---|
| 47 |
|
|---|
| [b60c60f] | 48 | /* Global list of nodes to be freed */
|
|---|
| 49 | free_t* free_list;
|
|---|
| [8265082] | 50 |
|
|---|
| 51 | void initialize(queue_t *Q) {
|
|---|
| 52 | node_t *node = (node_t*)malloc(sizeof(node_t));
|
|---|
| [b60c60f] | 53 |
|
|---|
| 54 | // why isn't this NULL? (isn't the free_list empty???)
|
|---|
| 55 | free_list=(free_t*)malloc(sizeof(free_t));
|
|---|
| [8265082] | 56 |
|
|---|
| [b60c60f] | 57 | free_list->node = NULL;
|
|---|
| 58 | free_list->next = NULL;
|
|---|
| 59 |
|
|---|
| [8265082] | 60 | node->next.ptr = NULL;
|
|---|
| 61 | node->next.count = 0;
|
|---|
| 62 | Q->Head.ptr = Q->Tail.ptr = node;
|
|---|
| 63 | }
|
|---|
| 64 |
|
|---|
| [b60c60f] | 65 | /* WHAT DOES THIS DO???? Adds the node to the free_list */
|
|---|
| 66 | /* free_later ? */
|
|---|
| 67 | void setFree(node_t* freeNode) {
|
|---|
| 68 | $atomic {
|
|---|
| 69 | free_t *temp = (free_t*)malloc(sizeof(free_t));
|
|---|
| [8265082] | 70 |
|
|---|
| 71 | temp->node = freeNode;
|
|---|
| [b60c60f] | 72 | temp->next = free_list->next;
|
|---|
| 73 | free_list->next = temp;
|
|---|
| [8265082] | 74 | }
|
|---|
| 75 | }
|
|---|
| 76 |
|
|---|
| [b60c60f] | 77 | /* what does this do ?
|
|---|
| 78 | free_all(); // deallocates all nodes in free_list
|
|---|
| 79 | */
|
|---|
| 80 | void deallocate() {
|
|---|
| 81 | free_t *list = free_list;
|
|---|
| [8265082] | 82 |
|
|---|
| [b60c60f] | 83 | while (list != NULL) {
|
|---|
| 84 | free_t *tmp = list->next;
|
|---|
| 85 |
|
|---|
| [8265082] | 86 | free(list->node);
|
|---|
| 87 | free(list);
|
|---|
| [b60c60f] | 88 | list = tmp;
|
|---|
| [8265082] | 89 | }
|
|---|
| 90 | }
|
|---|
| 91 |
|
|---|
| [b60c60f] | 92 | /* ??? */
|
|---|
| 93 | _Bool ptr_equal(pointer_t p1, pointer_t p2) {
|
|---|
| [8265082] | 94 | return (p1.ptr == p2.ptr) && (p1.count == p2.count);
|
|---|
| 95 | }
|
|---|
| 96 |
|
|---|
| [b60c60f] | 97 | /* ??? */
|
|---|
| [8265082] | 98 | _Bool CAS(pointer_t *dest, pointer_t oldval, pointer_t newval){
|
|---|
| 99 | $atomic {
|
|---|
| [b60c60f] | 100 | if (ptr_equal(*dest, oldval)) {
|
|---|
| [8265082] | 101 | *dest = newval;
|
|---|
| 102 | return true;
|
|---|
| 103 | }
|
|---|
| 104 | return false;
|
|---|
| 105 | }
|
|---|
| 106 | }
|
|---|
| 107 |
|
|---|
| 108 | void enqueue(queue_t *Q, int value) {
|
|---|
| 109 | pointer_t tail, next;
|
|---|
| 110 | node_t *node = (node_t*)malloc(sizeof(node_t));
|
|---|
| 111 | node->value = value;
|
|---|
| 112 | node->next.ptr = NULL;
|
|---|
| 113 |
|
|---|
| [b60c60f] | 114 | while (true) {
|
|---|
| 115 | tail = Q->Tail;
|
|---|
| 116 | next = tail.ptr->next;
|
|---|
| 117 | if (ptr_equal(tail, Q->Tail)) {
|
|---|
| [fbff773f] | 118 | if (next.ptr == NULL) {
|
|---|
| [b60c60f] | 119 | if (CAS(&tail.ptr->next,
|
|---|
| 120 | next,
|
|---|
| 121 | (pointer_t){node, next.count+1}))
|
|---|
| 122 | break;
|
|---|
| [8265082] | 123 | }
|
|---|
| [b60c60f] | 124 | else {
|
|---|
| 125 | CAS(&Q->Tail,
|
|---|
| 126 | tail,
|
|---|
| 127 | (pointer_t){next.ptr, tail.count+1});
|
|---|
| [8265082] | 128 | }
|
|---|
| [b60c60f] | 129 | }
|
|---|
| [8265082] | 130 | }
|
|---|
| [b60c60f] | 131 | CAS(&Q->Tail, tail, (pointer_t){node, tail.count+1});
|
|---|
| [8265082] | 132 | }
|
|---|
| 133 |
|
|---|
| 134 | _Bool dequeue(queue_t *Q, int *pvalue) {
|
|---|
| 135 | pointer_t head, tail, next;
|
|---|
| 136 |
|
|---|
| [b60c60f] | 137 | while (true) {
|
|---|
| 138 | head = Q->Head;
|
|---|
| 139 | tail = Q->Tail;
|
|---|
| 140 | next = head.ptr->next;
|
|---|
| 141 | if (ptr_equal(head, Q->Head)) {
|
|---|
| 142 | if (head.ptr == tail.ptr) {
|
|---|
| 143 | if (next.ptr == NULL)
|
|---|
| 144 | return false;
|
|---|
| 145 | // Tail is falling behind. Try to advance it:
|
|---|
| 146 | CAS(&Q->Tail,
|
|---|
| 147 | tail,
|
|---|
| 148 | (pointer_t){next.ptr, tail.count+1});
|
|---|
| [8265082] | 149 | }
|
|---|
| [b60c60f] | 150 | else {
|
|---|
| [8265082] | 151 | *pvalue = next.ptr->value;
|
|---|
| [b60c60f] | 152 | if (CAS(&Q->Head,
|
|---|
| 153 | head,
|
|---|
| 154 | (pointer_t){next.ptr, head.count+1}))
|
|---|
| 155 | break;
|
|---|
| [8265082] | 156 | }
|
|---|
| [b60c60f] | 157 | }
|
|---|
| [8265082] | 158 | }
|
|---|
| [b60c60f] | 159 | setFree(head.ptr);
|
|---|
| 160 | return true;
|
|---|
| [8265082] | 161 | }
|
|---|
| 162 |
|
|---|
| 163 | /******************** Tests **************************/
|
|---|
| 164 |
|
|---|
| [b60c60f] | 165 | /* Auxiliary function to determine whether an array of
|
|---|
| 166 | * n integers is a permutation of the numbers 0..n-1. */
|
|---|
| [8265082] | 167 | _Bool is_permutation(int n, int *data) {
|
|---|
| 168 | _Bool seen[n];
|
|---|
| 169 |
|
|---|
| 170 | for (int i=0; i<n; i++)
|
|---|
| 171 | seen[i] = 0;
|
|---|
| 172 | for (int i=0; i<n; i++) {
|
|---|
| 173 | int value = data[i];
|
|---|
| 174 |
|
|---|
| 175 | if (value < 0 || value >= n)
|
|---|
| 176 | return 0;
|
|---|
| 177 | if (seen[value])
|
|---|
| 178 | return 0;
|
|---|
| 179 | seen[value] = 1;
|
|---|
| 180 | }
|
|---|
| 181 | return 1;
|
|---|
| 182 | }
|
|---|
| 183 |
|
|---|
| [b60c60f] | 184 | /* A single thread enqueues 10 items and then dequeues
|
|---|
| 185 | * 10 times. Checks that the dequeued sequence is the
|
|---|
| 186 | * same as the enqueued sequence.
|
|---|
| 187 | */
|
|---|
| [fbff773f] | 188 | void sequentialTest() {
|
|---|
| [8265082] | 189 | int d;
|
|---|
| 190 | queue_t sq;
|
|---|
| 191 |
|
|---|
| 192 | initialize(&sq);
|
|---|
| 193 | for (int i = 0; i < 10; i++) {
|
|---|
| 194 | enqueue(&sq, i);
|
|---|
| 195 | }
|
|---|
| 196 | for (int i = 0; i < 10; i++) {
|
|---|
| 197 | _Bool result = dequeue(&sq, &d);
|
|---|
| 198 |
|
|---|
| 199 | assert(result);
|
|---|
| 200 | assert(d == i);
|
|---|
| 201 | }
|
|---|
| [b60c60f] | 202 | deallocate();
|
|---|
| [8265082] | 203 | free(sq.Head.ptr);
|
|---|
| 204 | }
|
|---|
| 205 |
|
|---|
| [b60c60f] | 206 | /* n threads executing concurrently; each does one enqueue
|
|---|
| 207 | * and one dequeue. Check that the result is a permutation
|
|---|
| 208 | * of the numbers 0..n-1.
|
|---|
| 209 | */
|
|---|
| [fbff773f] | 210 | void permuteTest(int n) {
|
|---|
| [8265082] | 211 | queue_t sq;
|
|---|
| 212 | int array[n];
|
|---|
| 213 |
|
|---|
| 214 | initialize(&sq);
|
|---|
| [b60c60f] | 215 | $parfor (int i: 0 .. (n-1)) {
|
|---|
| [8265082] | 216 | enqueue(&sq, i);
|
|---|
| 217 | dequeue(&sq, &array[i]);
|
|---|
| 218 | }
|
|---|
| 219 | assert(is_permutation(n, array));
|
|---|
| [b60c60f] | 220 | deallocate();
|
|---|
| [8265082] | 221 | free(sq.Head.ptr);
|
|---|
| 222 | }
|
|---|
| 223 |
|
|---|
| [b60c60f] | 224 |
|
|---|
| 225 | /* Checks that a sequence of integers is obtained
|
|---|
| 226 | * by interleaving blocks of integers.
|
|---|
| 227 | * It is assumed that there are nthreads threads,
|
|---|
| 228 | * each of which generates the integers
|
|---|
| 229 | * nvals*tid, ..., nvals*(tid+1)-1.
|
|---|
| 230 | */
|
|---|
| 231 | void assertFIFO(int nthreads, int nvals, int *data) {
|
|---|
| 232 | // for each thread, the next value you expect to see
|
|---|
| 233 | // from that thread:
|
|---|
| 234 | int expect[nthreads];
|
|---|
| 235 |
|
|---|
| 236 | for (int tid=0; tid<nthreads; tid++)
|
|---|
| 237 | expect[tid] = tid*nvals;
|
|---|
| 238 | for (int i=0; i< nthreads*nvals; i++) {
|
|---|
| 239 | int x = data[i];
|
|---|
| 240 | int tid = x/nvals;
|
|---|
| 241 |
|
|---|
| 242 | assert(expect[tid]==x);
|
|---|
| 243 | expect[tid]++;
|
|---|
| 244 | }
|
|---|
| 245 | }
|
|---|
| 246 |
|
|---|
| 247 | /* Tests the FIFO property where multiple threads enqueue
|
|---|
| 248 | * concurrently and a single thread dequeues everything.
|
|---|
| 249 | * t: number of threads, n: number of values each thread
|
|---|
| 250 | * will enqueue.
|
|---|
| 251 | */
|
|---|
| 252 | void test3(int nthreads, int nvals) {
|
|---|
| 253 | // the values dequeued by the single thread:
|
|---|
| 254 | int result[nthreads*nvals];
|
|---|
| [8265082] | 255 | queue_t sq;
|
|---|
| [b60c60f] | 256 |
|
|---|
| 257 | initialize(&sq);
|
|---|
| 258 | $parfor (int tid: 0 .. nthreads-1) {
|
|---|
| 259 | for (int i=0; i<nvals; i++) {
|
|---|
| 260 | enqueue(&sq, i+tid*nvals);
|
|---|
| [8265082] | 261 | }
|
|---|
| 262 | }
|
|---|
| [b60c60f] | 263 | printf("Dequeued: ");
|
|---|
| 264 | for (int i=0; i<nthreads*nvals; i++) {
|
|---|
| 265 | dequeue(&sq, &result[i]);
|
|---|
| 266 | printf("%d\t", result[i]);
|
|---|
| [8265082] | 267 | }
|
|---|
| 268 | printf("\n");
|
|---|
| [b60c60f] | 269 | assertFIFO(nthreads, nvals, result);
|
|---|
| 270 | deallocate();
|
|---|
| [8265082] | 271 | free(sq.Head.ptr);
|
|---|
| 272 | }
|
|---|
| 273 |
|
|---|
| 274 | void main() {
|
|---|
| [fbff773f] | 275 | sequentialTest();
|
|---|
| 276 | permuteTest(3);
|
|---|
| 277 | test3(2, 3);
|
|---|
| [8265082] | 278 | }
|
|---|