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 }