1
0

LockFreeFIFO.cpp 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. #include "LockFreeFIFO.h"
  2. #include "foundation/align.h"
  3. #include "foundation/atomics.h"
  4. #if 1 //def USE_VISTA_UP_API
  5. static int CAS2(FIFO_POINTER *fifo, queue_node_t *tail, size_t icount, queue_node_t *next, size_t icount2)
  6. {
  7. NALIGN(8) FIFO_POINTER compare = { tail, icount };
  8. NALIGN(8) FIFO_POINTER exchange = { next, icount2 };
  9. return nx_atomic_cmpxchg2(*(int64_t *)&compare, *(int64_t *)&exchange, (volatile int64_t *)fifo);
  10. //return atomic_compare_exchange_doublepointer((volatile intptr2_t *)fifo, *(intptr2_t *)&exchange, *(intptr2_t *)&compare);
  11. }
  12. #else
  13. inline char CAS2 (volatile void * addr, volatile void * v1, volatile long v2, void * n1, long n2)
  14. {
  15. register char c;
  16. __asm {
  17. push ebx
  18. push ecx
  19. push edx
  20. push esi
  21. mov esi, addr
  22. mov eax, v1
  23. mov ebx, n1
  24. mov ecx, n2
  25. mov edx, v2
  26. LOCK cmpxchg8b qword ptr [esi]
  27. sete c
  28. pop esi
  29. pop edx
  30. pop ecx
  31. pop ebx
  32. }
  33. return c;
  34. }
  35. #endif
  36. static int CAS(queue_node_t **fifo, queue_node_t *compare, queue_node_t *exchange)
  37. {
  38. return nx_atomic_cmpxchg_pointer(compare, exchange, (void* volatile *)fifo);
  39. }
  40. #define ENDFIFO(ff) ((queue_node_t*)0)
  41. void fifo_init(fifo_t *fifo)
  42. {
  43. fifo->dummy.Next = ENDFIFO(fifo); //makes the cell the only cell in the list
  44. fifo->head.fifo_node_t = fifo->tail.fifo_node_t = &fifo->dummy;
  45. fifo->head.count = fifo->tail.count = 0;
  46. fifo->count = 0;
  47. }
  48. void fifo_push(fifo_t *fifo, queue_node_t *cl)
  49. {
  50. queue_node_t * volatile tail;
  51. size_t icount;
  52. cl->Next = ENDFIFO(fifo); // // set the cell next pointer to end marker
  53. for (;;) // try until enqueue is done
  54. {
  55. icount = fifo->tail.count; // read the _tail modification count
  56. tail = fifo->tail.fifo_node_t; // read the _tail cell
  57. if (CAS(&tail->Next, ENDFIFO(fifo), cl)) // try to link the cell to the _tail cell
  58. {
  59. break; // enqueue is done, exit the loop
  60. }
  61. else // _tail was not pointing to the last cell, try to set _tail to the next cell
  62. {
  63. CAS2(&fifo->tail, tail, icount, tail->Next, icount+1);
  64. }
  65. }
  66. CAS2 (&fifo->tail, tail, icount, cl, icount+1); // enqueue is done, try to set _tail to the enqueued cell
  67. nx_atomic_inc(&fifo->count);
  68. }
  69. queue_node_t *fifo_pop(fifo_t *fifo)
  70. {
  71. queue_node_t * volatile head;
  72. for (;;)
  73. {
  74. size_t ocount = fifo->head.count; // read the _head modification count
  75. size_t icount = fifo->tail.count; // read the _tail modification count
  76. head = fifo->head.fifo_node_t; // read the _head cell
  77. queue_node_t *next = head->Next; // read the next cell
  78. if (ocount == fifo->head.count) // ensures that next is a valid pointer to avoid failure when reading next value
  79. {
  80. if (head == fifo->tail.fifo_node_t) // is queue empty or _tail falling behind ?
  81. {
  82. if (next == ENDFIFO(fifo)) // is queue empty ?
  83. {
  84. return 0; // queue is empty: return NULL
  85. }
  86. // _tail is pointing to _head in a non empty queue, try to set _tail to the next cell
  87. CAS2(&fifo->tail, head, icount, next, icount+1);
  88. }
  89. else if (next != ENDFIFO(fifo)) // if we are not competing on the dummy next
  90. {
  91. if (CAS2(&fifo->head, head, ocount, next, ocount+1)) // try to set _head to the next cell
  92. {
  93. break;
  94. }
  95. }
  96. }
  97. }
  98. nx_atomic_dec(&fifo->count);
  99. if (head == &fifo->dummy)
  100. {
  101. fifo_push(fifo,head);
  102. head = fifo_pop(fifo);
  103. }
  104. return head;
  105. }