rt: Remove lock_free_queue. Unused. Issue #2701
This commit is contained in:
parent
216105fc55
commit
3d0826b5fc
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Interrupt transparent queue, Schoen et. al, "On Interrupt-Transparent
|
||||
* Synchronization in an Embedded Object-Oriented Operating System", 2000.
|
||||
* enqueue() is allowed to interrupt enqueue() and dequeue(), however,
|
||||
* dequeue() is not allowed to interrupt itself.
|
||||
*/
|
||||
|
||||
#include "../rust_globals.h"
|
||||
#include "lock_free_queue.h"
|
||||
|
||||
lock_free_queue_node::lock_free_queue_node() : next(NULL) {
|
||||
|
||||
}
|
||||
|
||||
lock_free_queue::lock_free_queue() : _tail(this) {
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
lock_free_queue::enqueue(lock_free_queue_node *item) {
|
||||
lock.lock();
|
||||
item->next = (lock_free_queue_node *) NULL;
|
||||
lock_free_queue_node *last = _tail;
|
||||
_tail = item;
|
||||
while (last->next) {
|
||||
last = last->next;
|
||||
}
|
||||
last->next = item;
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
lock_free_queue_node *
|
||||
lock_free_queue::dequeue() {
|
||||
lock.lock();
|
||||
lock_free_queue_node *item = next;
|
||||
if (item && !(next = item->next)) {
|
||||
_tail = (lock_free_queue_node *) this;
|
||||
if (item->next) {
|
||||
lock_free_queue_node *lost = item->next;
|
||||
lock_free_queue_node *help;
|
||||
do {
|
||||
help = lost->next;
|
||||
enqueue(lost);
|
||||
} while ((lost = help) != (lock_free_queue_node *) NULL);
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
return item;
|
||||
}
|
||||
|
||||
bool
|
||||
lock_free_queue::is_empty() {
|
||||
return next == NULL;
|
||||
}
|
|
@ -1,212 +0,0 @@
|
|||
#ifndef LOCK_FREE_QUEUE_H
|
||||
#define LOCK_FREE_QUEUE_H
|
||||
|
||||
/**
|
||||
* How and why this lock free queue works:
|
||||
*
|
||||
* Adapted from the paper titled "Simple, Fast, and Practical Non-Blocking
|
||||
* and Blocking Concurrent Queue Algorithms" by Maged M. Michael,
|
||||
* Michael L. Scott.
|
||||
*
|
||||
* Safety Properties:
|
||||
*
|
||||
* 1. The linked list is always connected.
|
||||
* 2. Nodes are only inserted after the last node in the linked list.
|
||||
* 3. Nodes are only deleted from the beginning of the linked list.
|
||||
* 4. Head always points to the first node in the linked list.
|
||||
* 5. Tail always points to a node in the linked list.
|
||||
*
|
||||
*
|
||||
* 1. The linked list is always connected because the next pointer is not set
|
||||
* to null before the node is freed, and no node is freed until deleted
|
||||
* from the linked list.
|
||||
*
|
||||
* 2. Nodes are only inserted at the end of the linked list because they are
|
||||
* linked through the tail pointer which always points to a node in the
|
||||
* linked list (5) and an inserted node is only linked to a node that has
|
||||
* a null next pointer, and the only such node is the last one (1).
|
||||
*
|
||||
* 3. Nodes are deleted from the beginning of the list because they are
|
||||
* deleted only when they are pointed to by head which always points to the
|
||||
* first node (4).
|
||||
*
|
||||
* 4. Head always points to the first node in the list because it only changes
|
||||
* its value to the next node atomically. The new value of head cannot be
|
||||
* null because if there is only one node in the list the dequeue operation
|
||||
* returns without deleting any nodes.
|
||||
*
|
||||
* 5. Tail always points to a node in the linked list because it never lags
|
||||
* behind head, so it can never point to a deleted node. Also, when tail
|
||||
* changes its value it always swings to the next node in the list and it
|
||||
* never tires to change its value if the next pointer is NULL.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
template <class T>
|
||||
class lock_free_queue {
|
||||
|
||||
struct node_t;
|
||||
struct pointer_t {
|
||||
node_t *node;
|
||||
uint32_t count;
|
||||
pointer_t() : node(NULL), count(0) {
|
||||
}
|
||||
pointer_t(node_t *node, uint32_t count) {
|
||||
this->node = node;
|
||||
this->count = count;
|
||||
}
|
||||
bool equals(pointer_t &other) {
|
||||
return node == other.node && count == other.count;
|
||||
}
|
||||
};
|
||||
|
||||
struct node_t {
|
||||
T value;
|
||||
pointer_t next;
|
||||
|
||||
node_t() {
|
||||
next.node = NULL;
|
||||
next.count = 0;
|
||||
}
|
||||
|
||||
node_t(pointer_t next, T value) {
|
||||
this->next = next;
|
||||
this->value = value;
|
||||
}
|
||||
};
|
||||
|
||||
// Always points to the first node in the list.
|
||||
pointer_t head;
|
||||
|
||||
// Always points to a node in the list, (not necessarily the last).
|
||||
pointer_t tail;
|
||||
|
||||
// Compare and swap counted pointers, we can only do this if pointr_t is
|
||||
// 8 bytes or less since that the maximum size CAS can handle.
|
||||
bool compare_and_swap(pointer_t *address,
|
||||
pointer_t *oldValue,
|
||||
pointer_t newValue) {
|
||||
|
||||
// FIXME (#2701) this is requiring us to pass -fno-strict-aliasing
|
||||
// to GCC (possibly there are other, similar problems)
|
||||
if (sync::compare_and_swap(
|
||||
(uint64_t*) address,
|
||||
*(uint64_t*) oldValue,
|
||||
*(uint64_t*) &newValue)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
lock_free_queue() {
|
||||
// We can only handle 64bit CAS for counted pointers, so this will
|
||||
// not work with 64bit pointers.
|
||||
assert (sizeof(pointer_t) == sizeof(uint64_t));
|
||||
|
||||
// Allocate a dummy node to be used as the first node in the list.
|
||||
node_t *node = new node_t();
|
||||
|
||||
// Head and tail both start out pointing to the dummy node.
|
||||
head.node = node;
|
||||
tail.node = node;
|
||||
}
|
||||
|
||||
virtual ~lock_free_queue() {
|
||||
// Delete dummy node.
|
||||
delete head.node;
|
||||
}
|
||||
|
||||
bool is_empty() {
|
||||
return head.node == tail.node;
|
||||
}
|
||||
|
||||
virtual void enqueue(T value) {
|
||||
|
||||
// Create a new node to be inserted in the linked list, and set the
|
||||
// next node to NULL.
|
||||
node_t *node = new node_t();
|
||||
node->value = value;
|
||||
node->next.node = NULL;
|
||||
pointer_t tail;
|
||||
|
||||
// Keep trying until enqueue is done.
|
||||
while (true) {
|
||||
// Read the current tail which may either point to the last node
|
||||
// or to the second to last node (not sure why second to last,
|
||||
// and not any other node).
|
||||
tail = this->tail;
|
||||
|
||||
// Reads the next node after the tail which will be the last node
|
||||
// if null.
|
||||
pointer_t next;
|
||||
if (tail.node != NULL) {
|
||||
next = tail.node->next;
|
||||
}
|
||||
|
||||
// Loop if another thread changed the tail since we last read it.
|
||||
if (tail.equals(this->tail) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If next is not pointing to the last node try to swing tail to
|
||||
// the last node and loop.
|
||||
if (next.node != NULL) {
|
||||
compare_and_swap(&this->tail, &tail,
|
||||
pointer_t(next.node, tail.count + 1));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to link node at the end of the linked list.
|
||||
if (compare_and_swap(&tail.node->next, &next,
|
||||
pointer_t(node, next.count + 1))) {
|
||||
// Enqueueing is done.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Enqueue is done, try to swing tail to the inserted node.
|
||||
compare_and_swap(&this->tail, &tail,
|
||||
pointer_t(node, tail.count + 1));
|
||||
}
|
||||
|
||||
bool dequeue(T *value) {
|
||||
pointer_t head;
|
||||
|
||||
// Keep trying until dequeue is done.
|
||||
while(true) {
|
||||
head = this->head;
|
||||
pointer_t tail = this->tail;
|
||||
pointer_t next = head.node->next;
|
||||
|
||||
if (head.equals(this->head) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If queue is empty, or if tail is falling behind.
|
||||
if (head.node == tail.node) {
|
||||
// If queue is empty.
|
||||
if (next.node == NULL) {
|
||||
return false;
|
||||
}
|
||||
// Tail is falling behind, advance it.
|
||||
compare_and_swap(&this->tail,
|
||||
&tail,
|
||||
pointer_t(next.node, tail.count + 1));
|
||||
} else {
|
||||
// Read value before CAS, otherwise another
|
||||
// dequeue might advance it.
|
||||
*value = next.node->value;
|
||||
if (compare_and_swap(&this->head, &head,
|
||||
pointer_t(next.node, head.count + 1))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
delete head.node;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* LOCK_FREE_QUEUE_H */
|
Loading…
Reference in New Issue