rt: Remove lock_free_queue. Unused. Issue #2701
This commit is contained in:
parent
216105fc55
commit
3d0826b5fc
2 changed files with 0 additions and 266 deletions
|
@ -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…
Add table
Add a link
Reference in a new issue