aboutsummaryrefslogtreecommitdiff
path: root/Src/replicant/nu/LockFreeFIFO.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/replicant/nu/LockFreeFIFO.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/replicant/nu/LockFreeFIFO.cpp')
-rw-r--r--Src/replicant/nu/LockFreeFIFO.cpp110
1 files changed, 110 insertions, 0 deletions
diff --git a/Src/replicant/nu/LockFreeFIFO.cpp b/Src/replicant/nu/LockFreeFIFO.cpp
new file mode 100644
index 00000000..3731d7cd
--- /dev/null
+++ b/Src/replicant/nu/LockFreeFIFO.cpp
@@ -0,0 +1,110 @@
+#include "LockFreeFIFO.h"
+#include "foundation/align.h"
+#include "foundation/atomics.h"
+
+#if 1 //def USE_VISTA_UP_API
+static int CAS2(FIFO_POINTER *fifo, queue_node_t *tail, size_t icount, queue_node_t *next, size_t icount2)
+{
+ NALIGN(8) FIFO_POINTER compare = { tail, icount };
+ NALIGN(8) FIFO_POINTER exchange = { next, icount2 };
+ return nx_atomic_cmpxchg2(*(int64_t *)&compare, *(int64_t *)&exchange, (volatile int64_t *)fifo);
+ //return atomic_compare_exchange_doublepointer((volatile intptr2_t *)fifo, *(intptr2_t *)&exchange, *(intptr2_t *)&compare);
+}
+#else
+inline char CAS2 (volatile void * addr, volatile void * v1, volatile long v2, void * n1, long n2)
+{
+ register char c;
+ __asm {
+ push ebx
+ push ecx
+ push edx
+ push esi
+ mov esi, addr
+ mov eax, v1
+ mov ebx, n1
+ mov ecx, n2
+ mov edx, v2
+ LOCK cmpxchg8b qword ptr [esi]
+ sete c
+ pop esi
+ pop edx
+ pop ecx
+ pop ebx
+ }
+ return c;
+}
+#endif
+
+static int CAS(queue_node_t **fifo, queue_node_t *compare, queue_node_t *exchange)
+{
+ return nx_atomic_cmpxchg_pointer(compare, exchange, (void* volatile *)fifo);
+}
+
+#define ENDFIFO(ff) ((queue_node_t*)0)
+void fifo_init(fifo_t *fifo)
+{
+ fifo->dummy.Next = ENDFIFO(fifo); //makes the cell the only cell in the list
+ fifo->head.fifo_node_t = fifo->tail.fifo_node_t = &fifo->dummy;
+ fifo->head.count = fifo->tail.count = 0;
+ fifo->count = 0;
+}
+
+void fifo_push(fifo_t *fifo, queue_node_t *cl)
+{
+ queue_node_t * volatile tail;
+ size_t icount;
+ cl->Next = ENDFIFO(fifo); // // set the cell next pointer to end marker
+ for (;;) // try until enqueue is done
+ {
+ icount = fifo->tail.count; // read the _tail modification count
+ tail = fifo->tail.fifo_node_t; // read the _tail cell
+ if (CAS(&tail->Next, ENDFIFO(fifo), cl)) // try to link the cell to the _tail cell
+ {
+ break; // enqueue is done, exit the loop
+ }
+ else // _tail was not pointing to the last cell, try to set _tail to the next cell
+ {
+ CAS2(&fifo->tail, tail, icount, tail->Next, icount+1);
+ }
+ }
+ CAS2 (&fifo->tail, tail, icount, cl, icount+1); // enqueue is done, try to set _tail to the enqueued cell
+ nx_atomic_inc(&fifo->count);
+}
+
+queue_node_t *fifo_pop(fifo_t *fifo)
+{
+ queue_node_t * volatile head;
+ for (;;)
+ {
+ size_t ocount = fifo->head.count; // read the _head modification count
+ size_t icount = fifo->tail.count; // read the _tail modification count
+ head = fifo->head.fifo_node_t; // read the _head cell
+ queue_node_t *next = head->Next; // read the next cell
+ if (ocount == fifo->head.count) // ensures that next is a valid pointer to avoid failure when reading next value
+ {
+ if (head == fifo->tail.fifo_node_t) // is queue empty or _tail falling behind ?
+ {
+ if (next == ENDFIFO(fifo)) // is queue empty ?
+ {
+ return 0; // queue is empty: return NULL
+ }
+ // _tail is pointing to _head in a non empty queue, try to set _tail to the next cell
+ CAS2(&fifo->tail, head, icount, next, icount+1);
+ }
+ else if (next != ENDFIFO(fifo)) // if we are not competing on the dummy next
+ {
+ if (CAS2(&fifo->head, head, ocount, next, ocount+1)) // try to set _head to the next cell
+ {
+ break;
+ }
+ }
+ }
+ }
+ nx_atomic_dec(&fifo->count);
+ if (head == &fifo->dummy)
+ {
+ fifo_push(fifo,head);
+ head = fifo_pop(fifo);
+ }
+ return head;
+}