1 module concurrency.slist; 2 3 import core.atomic : atomicLoad, atomicExchange; 4 import concurrency.utils : casWeak; 5 6 /// A lock-free single linked list 7 shared final class SList(T) { 8 struct Node { 9 shared(Node*) next; 10 T payload; 11 } 12 Node* head; 13 14 void pushFront(T payload) @trusted { 15 auto node = new shared Node(atomicLoad(head), cast(shared(T))payload); 16 while (!casWeak(&head, node.next, node)) 17 node.next = atomicLoad(head); 18 } 19 20 void pushBack(T payload) @trusted { 21 auto node = new shared Node(null, cast(shared(T))payload); 22 while (true) { 23 auto last = getLast(); 24 if (last is null) { 25 if (casWeak(&head, cast(shared(Node*))null, node)) 26 return; 27 } else { 28 auto lastNext = atomicLoad(last.next); 29 if (!isMarked(lastNext) && casWeak(&last.next, cast(shared(Node*))null, node)) 30 return; 31 } 32 } 33 } 34 35 auto release() @safe { 36 auto old = atomicExchange(&head, cast(shared(Node*))null); 37 return Iterator!T(old); 38 } 39 40 private shared(Node)* getLast() @trusted { 41 auto current = atomicLoad(head); 42 if (current is null) 43 return null; 44 while (true) { 45 auto next = clearMark(atomicLoad(current.next)); 46 if (next is null) 47 break; 48 current = next; 49 } 50 return current; 51 } 52 53 auto opSlice() return @safe { 54 return Iterator!T(atomicLoad(head)); 55 } 56 57 bool remove(T payload) @trusted { 58 auto iter = this[]; 59 while (!iter.empty) { 60 if (iter.front == cast(shared(T))payload) { 61 if (remove(iter.node)) 62 return true; 63 } 64 iter.popFront(); 65 } 66 return false; 67 } 68 69 private bool remove(shared(Node*) node) @trusted { 70 // step 1, mark the next ptr to signify this one is logically deleted 71 shared(Node*) ptr; 72 shared(Node*)* currentNext; 73 do { 74 ptr = atomicLoad(node.next); 75 if (isMarked(ptr)) 76 return false; 77 } while (!casWeak(&node.next, ptr, mark(ptr))); 78 // step 2, iterate until next points to node, then cas 79 retry: 80 currentNext = &head; 81 while (true) { 82 ptr = atomicLoad(*currentNext); 83 if (clearMark(ptr) is null) 84 return false; 85 if (clearMark(ptr) is node) { 86 if (isMarked(ptr)) 87 goto retry; 88 89 if (casWeak(currentNext, ptr, clearMark(node.next))) 90 return true; 91 goto retry; 92 } 93 currentNext = &clearMark(ptr).next; 94 } 95 assert(0); 96 } 97 } 98 99 static bool isMarked(T)(T* p) 100 { 101 return (cast(size_t)p & 1) != 0; 102 } 103 104 static T* mark(T)(T* p) 105 { 106 return cast(T*)(cast(size_t)p | 1); 107 } 108 109 static T* clearMark(T)(T* p) 110 { 111 return cast(T*)(cast(size_t)p & ~1); 112 } 113 114 struct Iterator(T) { 115 alias Node = SList!T.Node; 116 shared(Node*) current; 117 this(shared(Node*) head) @safe nothrow { 118 current = head; 119 } 120 bool empty() @trusted nothrow { 121 if (current is null) 122 return true; 123 while (isMarked(current.next)) { 124 current = clearMark(atomicLoad(current.next)); 125 if (current is null) 126 return true; 127 } 128 return false; 129 } 130 shared(T) front() @safe nothrow { 131 return current.payload; 132 } 133 shared(Node*) node() @safe nothrow { 134 return current; 135 } 136 void popFront() @trusted nothrow { 137 current = clearMark(atomicLoad(current.next)); 138 } 139 }