/* * File : lock_free_queue.c * Author : Jérémy Zurcher * 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; }