diff options
Diffstat (limited to 'lock_free_queue.c')
-rw-r--r-- | lock_free_queue.c | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/lock_free_queue.c b/lock_free_queue.c new file mode 100644 index 0000000..217fb0b --- /dev/null +++ b/lock_free_queue.c @@ -0,0 +1,88 @@ +/* + * File : lock_free_queue.c + * Author : Jérémy Zurcher <jeremy@asynk.ch> + * Date : 01/11/09 + * License : stolen from http://www.cs.rochester.edu/~scott/papers/1996_PODC_queues.pdf + */ + +#include "stdlib.h" +#include "cas.h" +#include "lock_free_queue.h" +#ifdef DEBUG +#include "stdio.h" +#endif + +void init( lfq_t *q ) { + node_t *node = (node_t*)malloc(sizeof(node_t)); + node->next.split.ptr = NULL; + node->next.split.count = 0; + q->head.split.ptr = q->tail.split.ptr = node; + q->head.split.count = q->tail.split.count = 0; +} + +void enqueue( lfq_t *q, void *data ) { + node_t *node; + pointer_t tail; + pointer_t next; + pointer_t tmp; + + node = (node_t*)malloc(sizeof(node_t)); + node->data = data; + node->next.split.ptr = NULL; + /* node->next.split.count = 0; */ + for(;;) { + tail.concat = q->tail.concat; /* copy tail pointer */ + next.concat = tail.split.ptr->next.concat; /* copy next pointer */ + if (tail.concat == q->tail.concat) { /* tail is still consistent */ + if (next.split.ptr == NULL) { /* next is still the last node */ + /* if tail->next is the same as next, link node at the end of the list */ + tmp.split.ptr = node; + tmp.split.count = next.split.count+1; + if ( compare_and_swap( &tail.split.ptr->next.concat, next.concat, tmp.concat ) ) break; + } else { + /* try to swing tail to the next node, if q-> tail is still tail => next is ok */ + tmp.split.ptr = next.split.ptr; + tmp.split.count = tail.split.count+1; + compare_and_swap( &q->tail.concat, tail.concat, tmp.concat ); + } + } + } + /* try to swing tail to the next node, may have been done by a concurrent push */ + tmp.split.ptr = node; + tmp.split.count = tail.split.count+1; + compare_and_swap( &q->tail.concat, tail.concat, tmp.concat ); +} + +void* dequeue( lfq_t *q ) { + void *data; + pointer_t head; + pointer_t tail; + node_t *next; + pointer_t tmp; + + for(;;) { + head.concat = q->head.concat; + tail.concat = q->tail.concat; + next = (node_t *)head.split.ptr; + if (head.concat == q->head.concat) { + if (head.split.ptr == tail.split.ptr) { /* still consistent */ + if (next->next.split.ptr == NULL) { /* queue empty */ + return NULL; + } + /* new node has been linked, but tail is behind, should advance it */ + tmp.split.ptr = next->next.split.ptr; + tmp.split.count = tail.split.count+1; + compare_and_swap( &q->tail.concat, tail.concat, tmp.concat ); + } else { + data = next->data; + /* try to swing head to the next node */ + tmp.split.ptr = next->next.split.ptr; + tmp.split.count = head.split.count+1; + if( compare_and_swap( &q->head.concat, head.concat, tmp.concat ) ) break; + } + } + } + free((void*)head.split.ptr); + return data; +} + |