queue = new LinkedBlockingQueue<>();
15 | private Thread thread;
16 |
17 | @Override
18 | public void schedule(Runnable task, Object actorId) {
19 | queue.add(task);
20 | }
21 |
22 | @Override
23 | public void close() {
24 | thread.interrupt();
25 | }
26 |
27 | /**
28 | * Starts message processing loop in the current thread. This method does not return until the scheduler is disposed by calling {@link #close()}. If {@link #schedule(Runnable, Object)} is called before start(), the message will be kept
29 | * in the queue.
30 | */
31 | public void start() {
32 | this.thread = Thread.currentThread();
33 | try {
34 | while (!thread.isInterrupted()) {
35 | Runnable job = queue.take();
36 | job.run();
37 | }
38 | } catch (InterruptedException e) {
39 | Thread.currentThread().interrupt();
40 | }
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/zakgof/actr/impl/ConcurrentDoublyLinkedList.java:
--------------------------------------------------------------------------------
1 | package com.zakgof.actr.impl;
2 |
3 | /*
4 | * Written by Doug Lea with assistance from members of JCP JSR-166
5 | * Expert Group and released to the private domain, as explained at
6 | * http://creativecommons.org/licenses/privatedomain
7 | */
8 |
9 | import java.util.AbstractCollection;
10 | import java.util.ArrayList;
11 | import java.util.Collection;
12 | import java.util.ConcurrentModificationException;
13 | import java.util.Deque;
14 | import java.util.Iterator;
15 | import java.util.NoSuchElementException;
16 | import java.util.concurrent.atomic.AtomicReference;
17 | import java.util.function.IntFunction;
18 |
19 | /**
20 | * A concurrent linked-list implementation of a {@link Deque} (double-ended queue). Concurrent insertion, removal, and access operations execute safely across multiple threads. Iterators are weakly consistent, returning elements reflecting the
21 | * state of the deque at some point at or since the creation of the iterator. They do not throw {@link ConcurrentModificationException}, and may proceed concurrently with other operations.
22 | *
23 | *
24 | * This class and its iterators implement all of the optional methods of the {@link Collection} and {@link Iterator} interfaces. Like most other concurrent collection implementations, this class does not permit the use of null
25 | * elements. because some null arguments and return values cannot be reliably distinguished from the absence of elements. Arbitrarily, the {@link Collection#remove} method is mapped to removeFirstOccurrence, and {@link Collection#add} is
26 | * mapped to addLast.
27 | *
28 | *
29 | * Beware that, unlike in most collections, the size method is NOT a constant-time operation. Because of the asynchronous nature of these deques, determining the current number of elements requires a traversal of the elements.
30 | *
31 | *
32 | * This class is Serializable, but relies on default serialization mechanisms. Usually, it is a better idea for any serializable class using a ConcurrentLinkedDeque to instead serialize a snapshot of the elements obtained by method
33 | * toArray.
34 | *
35 | * @author Doug Lea
36 | * @param the type of elements held in this collection
37 | */
38 |
39 | class ConcurrentDoublyLinkedList extends AbstractCollection
40 | implements java.io.Serializable {
41 | @Override
42 | public T[] toArray(IntFunction generator) {
43 | return super.toArray(generator);
44 | }
45 |
46 | /*
47 | * This is an adaptation of an algorithm described in Paul Martin's "A Practical Lock-Free Doubly-Linked List". Sun Labs Tech report. The basic idea is to primarily rely on next-pointers to ensure consistency. Prev-pointers are in part
48 | * optimistic, reconstructed using forward pointers as needed. The main forward list uses a variant of HM-list algorithm similar to the one used in ConcurrentSkipListMap class, but a little simpler. It is also basically similar to the approach in
49 | * Edya Ladan-Mozes and Nir Shavit "An Optimistic Approach to Lock-Free FIFO Queues" in DISC04.
50 | *
51 | * Quoting a summary in Paul Martin's tech report:
52 | *
53 | * All cleanups work to maintain these invariants: (1) forward pointers are the ground truth. (2) forward pointers to dead nodes can be improved by swinging them further forward around the dead node. (2.1) forward pointers are still correct when
54 | * pointing to dead nodes, and forward pointers from dead nodes are left as they were when the node was deleted. (2.2) multiple dead nodes may point forward to the same node. (3) backward pointers were correct when they were installed (3.1)
55 | * backward pointers are correct when pointing to any node which points forward to them, but since more than one forward pointer may point to them, the live one is best. (4) backward pointers that are out of date due to deletion point to a
56 | * deleted node, and need to point further back until they point to the live node that points to their source. (5) backward pointers that are out of date due to insertion point too far backwards, so shortening their scope (by searching forward)
57 | * fixes them. (6) backward pointers from a dead node cannot be "improved" since there may be no live node pointing forward to their origin. (However, it does no harm to try to improve them while racing with a deletion.)
58 | *
59 | *
60 | * Notation guide for local variables n, b, f : a node, its predecessor, and successor s : some other successor
61 | */
62 |
63 | // Minor convenience utilities
64 |
65 | /**
66 | * Returns true if given reference is non-null and isn't a header, trailer, or marker.
67 | *
68 | * @param n (possibly null) node
69 | * @return true if n exists as a user node
70 | */
71 | private static boolean usable(Node> n) {
72 | return n != null && !n.isSpecial();
73 | }
74 |
75 | /**
76 | * Throws NullPointerException if argument is null
77 | *
78 | * @param v the element
79 | */
80 | private static void checkNullArg(Object v) {
81 | if (v == null)
82 | throw new NullPointerException();
83 | }
84 |
85 | /**
86 | * Creates an array list and fills it with elements of this list. Used by toArray.
87 | *
88 | * @return the arrayList
89 | */
90 | private ArrayList toArrayList() {
91 | ArrayList c = new ArrayList<>();
92 | for (Node n = header.forward(); n != null; n = n.forward())
93 | c.add(n.element);
94 | return c;
95 | }
96 |
97 | // Fields and constructors
98 |
99 | private static final long serialVersionUID = 876323262645176354L;
100 |
101 | /**
102 | * List header. First usable node is at header.forward().
103 | */
104 | private final Node header;
105 |
106 | /**
107 | * List trailer. Last usable node is at trailer.back().
108 | */
109 | private final Node trailer;
110 |
111 | /**
112 | * Constructs an empty deque.
113 | */
114 | ConcurrentDoublyLinkedList() {
115 | Node h = new Node<>(null, null, null);
116 | Node t = new Node<>(null, null, h);
117 | h.setNext(t);
118 | header = h;
119 | trailer = t;
120 | }
121 |
122 | /**
123 | * Appends the given element to the end of this deque. This is identical in function to the add method.
124 | *
125 | * @param o the element to be inserted at the end of this deque.
126 | * @throws NullPointerException if the specified element is null
127 | */
128 | private void addLast(E o) {
129 | checkNullArg(o);
130 | while (trailer.prepend(o) == null)
131 | ;
132 | }
133 |
134 | Node coolAdd(E element) {
135 | Node node;
136 | while ((node = trailer.prepend(element)) == null);
137 | return node;
138 | }
139 |
140 | /**
141 | * Appends the given element to the end of this deque. (Identical in function to the add method; included only for consistency.)
142 | *
143 | * @param o the element to be inserted at the end of this deque.
144 | * @return true always
145 | * @throws NullPointerException if the specified element is null
146 | */
147 | private boolean offerLast(E o) {
148 | addLast(o);
149 | return true;
150 | }
151 |
152 | /**
153 | * Retrieves and removes the first element of this deque, or returns null if this deque is empty.
154 | *
155 | * @return the first element of this deque, or null if empty.
156 | */
157 | private E pollFirst() {
158 | for (;;) {
159 | Node n = header.successor();
160 | if (!usable(n))
161 | return null;
162 | if (n.delete())
163 | return n.element;
164 | }
165 | }
166 |
167 | @Override
168 | public boolean add(E e) {
169 | return offerLast(e);
170 | }
171 |
172 | /**
173 | * Removes the first element e such that o.equals(e), if such an element exists in this deque. If the deque does not contain the element, it is unchanged.
174 | *
175 | * @param o element to be removed from this deque, if present.
176 | * @return true if the deque contained the specified element.
177 | * @throws NullPointerException if the specified element is null
178 | */
179 | private boolean removeFirstOccurrence(Object o) {
180 | checkNullArg(o);
181 | for (;;) {
182 | Node n = header.forward();
183 | for (;;) {
184 | if (n == null)
185 | return false;
186 | if (o.equals(n.element)) {
187 | if (n.delete())
188 | return true;
189 | else
190 | break; // restart if interference
191 | }
192 | n = n.forward();
193 | }
194 | }
195 | }
196 |
197 | /**
198 | * Returns true if this deque contains at least one element e such that o.equals(e).
199 | *
200 | * @param o element whose presence in this deque is to be tested.
201 | * @return true if this deque contains the specified element.
202 | */
203 | @Override
204 | public boolean contains(Object o) {
205 | if (o == null)
206 | return false;
207 | for (Node n = header.forward(); n != null; n = n.forward())
208 | if (o.equals(n.element))
209 | return true;
210 | return false;
211 | }
212 |
213 | /**
214 | * Returns true if this collection contains no elements.
215 | *
216 | *
217 | * @return true if this collection contains no elements.
218 | */
219 | @Override
220 | public boolean isEmpty() {
221 | return !usable(header.successor());
222 | }
223 |
224 | /**
225 | * Returns the number of elements in this deque. If this deque contains more than Integer.MAX_VALUE elements, it returns Integer.MAX_VALUE.
226 | *
227 | *
228 | * Beware that, unlike in most collections, this method is NOT a constant-time operation. Because of the asynchronous nature of these deques, determining the current number of elements requires traversing them all to count them.
229 | * Additionally, it is possible for the size to change during execution of this method, in which case the returned result will be inaccurate. Thus, this method is typically not very useful in concurrent applications.
230 | *
231 | * @return the number of elements in this deque.
232 | */
233 | @Override
234 | public int size() {
235 | long count = 0;
236 | for (Node n = header.forward(); n != null; n = n.forward())
237 | ++count;
238 | return (count >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) count;
239 | }
240 |
241 | /**
242 | * Removes the first element e such that o.equals(e), if such an element exists in this deque. If the deque does not contain the element, it is unchanged.
243 | *
244 | * @param o element to be removed from this deque, if present.
245 | * @return true if the deque contained the specified element.
246 | * @throws NullPointerException if the specified element is null
247 | */
248 | @Override
249 | public boolean remove(Object o) {
250 | return removeFirstOccurrence(o);
251 | }
252 |
253 | /**
254 | * Appends all of the elements in the specified collection to the end of this deque, in the order that they are returned by the specified collection's iterator. The behavior of this operation is undefined if the specified collection is modified
255 | * while the operation is in progress. (This implies that the behavior of this call is undefined if the specified Collection is this deque, and this deque is nonempty.)
256 | *
257 | * @param c the elements to be inserted into this deque.
258 | * @return true if this deque changed as a result of the call.
259 | * @throws NullPointerException if c or any element within it is null
260 | */
261 | @Override
262 | public boolean addAll(Collection extends E> c) {
263 | Iterator extends E> it = c.iterator();
264 | if (!it.hasNext())
265 | return false;
266 | do {
267 | addLast(it.next());
268 | } while (it.hasNext());
269 | return true;
270 | }
271 |
272 | /**
273 | * Removes all of the elements from this deque.
274 | */
275 | @Override
276 | public void clear() {
277 | while (pollFirst() != null)
278 | ;
279 | }
280 |
281 | /**
282 | * Returns an array containing all of the elements in this deque in the correct order.
283 | *
284 | * @return an array containing all of the elements in this deque in the correct order.
285 | */
286 | @Override
287 | public Object[] toArray() {
288 | return toArrayList().toArray();
289 | }
290 |
291 | /**
292 | * Returns an array containing all of the elements in this deque in the correct order; the runtime type of the returned array is that of the specified array. If the deque fits in the specified array, it is returned therein. Otherwise, a new array
293 | * is allocated with the runtime type of the specified array and the size of this deque.
294 | *
295 | *
296 | * If the deque fits in the specified array with room to spare (i.e., the array has more elements than the deque), the element in the array immediately following the end of the collection is set to null. This is useful in determining the length
297 | * of the deque only if the caller knows that the deque does not contain any null elements.
298 | *
299 | * @param a the array into which the elements of the deque are to be stored, if it is big enough; otherwise, a new array of the same runtime type is allocated for this purpose.
300 | * @return an array containing the elements of the deque.
301 | * @throws ArrayStoreException if the runtime type of a is not a supertype of the runtime type of every element in this deque.
302 | * @throws NullPointerException if the specified array is null.
303 | */
304 | @Override
305 | public T[] toArray(T[] a) {
306 | return toArrayList().toArray(a);
307 | }
308 |
309 | /**
310 | * Returns a weakly consistent iterator over the elements in this deque, in first-to-last order. The next method returns elements reflecting the state of the deque at some point at or since the creation of the iterator. The method does
311 | * not throw {@link ConcurrentModificationException}, and may proceed concurrently with other operations.
312 | *
313 | * @return an iterator over the elements in this deque
314 | */
315 | @Override
316 | public Iterator iterator() {
317 | return new CLDIterator();
318 | }
319 |
320 | final class CLDIterator implements Iterator {
321 | Node last;
322 |
323 | Node next = header.forward();
324 |
325 | @Override
326 | public boolean hasNext() {
327 | return next != null;
328 | }
329 |
330 | @Override
331 | public E next() {
332 | Node l = last = next;
333 | if (l == null)
334 | throw new NoSuchElementException();
335 | next = next.forward();
336 | return l.element;
337 | }
338 |
339 | @Override
340 | public void remove() {
341 | Node l = last;
342 | if (l == null)
343 | throw new IllegalStateException();
344 | while (!l.delete() && !l.isDeleted())
345 | ;
346 | }
347 | }
348 |
349 | }
350 |
351 | /**
352 | * Linked Nodes. As a minor efficiency hack, this class opportunistically inherits from AtomicReference, with the atomic ref used as the "next" link.
353 | *
354 | * Nodes are in doubly-linked lists. There are three kinds of special nodes, distinguished by: * The list header has a null prev link * The list trailer has a null next link * A deletion marker has a prev link pointing to itself. All three kinds of
355 | * special nodes have null element fields.
356 | *
357 | * Regular nodes have non-null element, next, and prev fields. To avoid visible inconsistencies when deletions overlap element replacement, replacements are done by replacing the node, not just setting the element.
358 | *
359 | * Nodes can be traversed by read-only ConcurrentLinkedDeque class operations just by following raw next pointers, so long as they ignore any special nodes seen along the way. (This is automated in method forward.) However, traversal using prev
360 | * pointers is not guaranteed to see all live nodes since a prev pointer of a deleted node can become unrecoverably stale.
361 | */
362 |
363 | @SuppressWarnings("serial")
364 | class Node extends AtomicReference> {
365 | private volatile Node prev;
366 |
367 | final E element;
368 |
369 | /** Creates a node with given contents */
370 | Node(E element, Node next, Node prev) {
371 | super(next);
372 | this.prev = prev;
373 | this.element = element;
374 | }
375 |
376 | /** Creates a marker node with given successor */
377 | Node(Node next) {
378 | super(next);
379 | this.prev = this;
380 | this.element = null;
381 | }
382 |
383 | /**
384 | * Gets next link (which is actually the value held as atomic reference).
385 | */
386 | private Node getNext() {
387 | return get();
388 | }
389 |
390 | /**
391 | * Sets next link
392 | *
393 | * @param n the next node
394 | */
395 | void setNext(Node n) {
396 | set(n);
397 | }
398 |
399 | /**
400 | * compareAndSet next link
401 | */
402 | private boolean casNext(Node cmp, Node val) {
403 | return compareAndSet(cmp, val);
404 | }
405 |
406 | /**
407 | * Gets prev link
408 | */
409 | private Node getPrev() {
410 | return prev;
411 | }
412 |
413 | /**
414 | * Sets prev link
415 | *
416 | * @param b the previous node
417 | */
418 | void setPrev(Node b) {
419 | prev = b;
420 | }
421 |
422 | /**
423 | * Returns true if this is a header, trailer, or marker node
424 | */
425 | boolean isSpecial() {
426 | return element == null;
427 | }
428 |
429 | /**
430 | * Returns true if this is a trailer node
431 | */
432 | boolean isTrailer() {
433 | return getNext() == null;
434 | }
435 |
436 | /**
437 | * Returns true if this is a header node
438 | */
439 | boolean isHeader() {
440 | return getPrev() == null;
441 | }
442 |
443 | /**
444 | * Returns true if this is a marker node
445 | */
446 | boolean isMarker() {
447 | return getPrev() == this;
448 | }
449 |
450 | /**
451 | * Returns true if this node is followed by a marker, meaning that it is deleted.
452 | *
453 | * @return true if this node is deleted
454 | */
455 | boolean isDeleted() {
456 | Node f = getNext();
457 | return f != null && f.isMarker();
458 | }
459 |
460 | /**
461 | * Returns next node, ignoring deletion marker
462 | */
463 | private Node nextNonmarker() {
464 | Node f = getNext();
465 | return (f == null || !f.isMarker()) ? f : f.getNext();
466 | }
467 |
468 | /**
469 | * Returns the next non-deleted node, swinging next pointer around any encountered deleted nodes, and also patching up successor''s prev link to point back to this. Returns null if this node is trailer so has no successor.
470 | *
471 | * @return successor, or null if no such
472 | */
473 | Node successor() {
474 | Node f = nextNonmarker();
475 | for (;;) {
476 | if (f == null)
477 | return null;
478 | if (!f.isDeleted()) {
479 | if (f.getPrev() != this && !isDeleted())
480 | f.setPrev(this); // relink f's prev
481 | return f;
482 | }
483 | Node s = f.nextNonmarker();
484 | if (f == getNext())
485 | casNext(f, s); // unlink f
486 | f = s;
487 | }
488 | }
489 |
490 | /**
491 | * Returns the apparent predecessor of target by searching forward for it starting at this node, patching up pointers while traversing. Used by predecessor().
492 | *
493 | * @return target's predecessor, or null if not found
494 | */
495 | private Node findPredecessorOf(Node target) {
496 | Node n = this;
497 | for (;;) {
498 | Node f = n.successor();
499 | if (f == target)
500 | return n;
501 | if (f == null)
502 | return null;
503 | n = f;
504 | }
505 | }
506 |
507 | /**
508 | * Returns the previous non-deleted node, patching up pointers as needed. Returns null if this node is header so has no successor. May also return null if this node is deleted, so doesn't have a distinct predecessor.
509 | *
510 | * @return predecessor or null if not found
511 | */
512 | Node predecessor() {
513 | Node n = this;
514 | for (;;) {
515 | Node b = n.getPrev();
516 | if (b == null)
517 | return n.findPredecessorOf(this);
518 | Node s = b.getNext();
519 | if (s == this)
520 | return b;
521 | if (s == null || !s.isMarker()) {
522 | Node p = b.findPredecessorOf(this);
523 | if (p != null)
524 | return p;
525 | }
526 | n = b;
527 | }
528 | }
529 |
530 | /**
531 | * Returns the next node containing a nondeleted user element. Use for forward list traversal.
532 | *
533 | * @return successor, or null if no such
534 | */
535 | Node forward() {
536 | Node f = successor();
537 | return (f == null || f.isSpecial()) ? null : f;
538 | }
539 |
540 | /**
541 | * Returns previous node containing a nondeleted user element, if possible. Use for backward list traversal, but beware that if this method is called from a deleted node, it might not be able to determine a usable predecessor.
542 | *
543 | * @return predecessor, or null if no such could be found
544 | */
545 | Node back() {
546 | Node f = predecessor();
547 | return (f == null || f.isSpecial()) ? null : f;
548 | }
549 |
550 | /**
551 | * Tries to insert a node holding element as successor, failing if this node is deleted.
552 | *
553 | * @param element the element
554 | * @return the new node, or null on failure.
555 | */
556 | Node append(E element) {
557 | for (;;) {
558 | Node f = getNext();
559 | if (f == null || f.isMarker())
560 | return null;
561 | Node x = new Node<>(element, f, this);
562 | if (casNext(f, x)) {
563 | f.setPrev(x); // optimistically link
564 | return x;
565 | }
566 | }
567 | }
568 |
569 | /**
570 | * Tries to insert a node holding element as predecessor, failing if no live predecessor can be found to link to.
571 | *
572 | * @param element the element
573 | * @return the new node, or null on failure.
574 | */
575 | Node prepend(E element) {
576 | for (;;) {
577 | Node b = predecessor();
578 | if (b == null)
579 | return null;
580 | Node x = new Node<>(element, this, b);
581 | if (b.casNext(this, x)) {
582 | setPrev(x); // optimistically link
583 | return x;
584 | }
585 | }
586 | }
587 |
588 |
589 | /**
590 | * Tries to mark this node as deleted, failing if already deleted or if this node is header or trailer
591 | *
592 | * @return true if successful
593 | */
594 | boolean delete() {
595 | Node b = getPrev();
596 | Node f = getNext();
597 | if (b != null && f != null && !f.isMarker()
598 | && casNext(f, new Node<>(f))) {
599 | if (b.casNext(this, f))
600 | f.setPrev(b);
601 | return true;
602 | }
603 | return false;
604 | }
605 |
606 | /**
607 | * Tries to insert a node holding element to replace this node. failing if already deleted.
608 | *
609 | * @param newElement the new element
610 | * @return the new node, or null on failure.
611 | */
612 | Node replace(E newElement) {
613 | for (;;) {
614 | Node b = getPrev();
615 | Node f = getNext();
616 | if (b == null || f == null || f.isMarker())
617 | return null;
618 | Node x = new Node<>(newElement, f, b);
619 | if (casNext(f, new Node<>(x))) {
620 | b.successor(); // to relink b
621 | x.successor(); // to relink f
622 | return x;
623 | }
624 | }
625 | }
626 | }
627 |
--------------------------------------------------------------------------------
/src/main/java/com/zakgof/actr/impl/ExecutorBasedScheduler.java:
--------------------------------------------------------------------------------
1 | package com.zakgof.actr.impl;
2 |
3 | import java.util.Queue;
4 | import java.util.concurrent.ConcurrentLinkedQueue;
5 | import java.util.concurrent.ExecutorService;
6 | import java.util.concurrent.atomic.AtomicInteger;
7 |
8 | import com.zakgof.actr.IActorScheduler;
9 |
10 | /**
11 | * Scheduler based on a provided executor service.
12 | */
13 | public class ExecutorBasedScheduler implements IActorScheduler {
14 |
15 | private final int throughput;
16 | private final ExecutorService executor;
17 |
18 | private volatile boolean shutdown = false;
19 |
20 | public ExecutorBasedScheduler(ExecutorService executor, int throughput) {
21 | this.executor = executor;
22 | this.throughput = throughput;
23 | }
24 |
25 | private static class Mailbox {
26 | private final Queue queue = new ConcurrentLinkedQueue<>();
27 | private final AtomicInteger queued = new AtomicInteger(0);
28 | }
29 |
30 | @Override
31 | public void actorCreated(Object actorId) {
32 | ((ActorImpl>) actorId).box(new Mailbox());
33 | }
34 |
35 | @Override
36 | public void actorDisposed(Object actorId) {
37 | ((ActorImpl>) actorId).box(null);
38 | }
39 |
40 | @Override
41 | public void schedule(Runnable raw, Object actorId) {
42 |
43 | if (shutdown) {
44 | return;
45 | }
46 |
47 | Runnable task = () -> {
48 | Mailbox mailbox = (Mailbox) ((ActorImpl>) actorId).box();
49 | mailbox.queue.add(raw);
50 | int before = mailbox.queued.getAndIncrement();
51 | if (before == 0) {
52 | processMailbox(mailbox);
53 | }
54 | };
55 | executor.execute(task);
56 | }
57 |
58 | private void processMailbox(Mailbox mailbox) {
59 | int processed = 0;
60 | for (;;) {
61 | Runnable runnable = mailbox.queue.poll();
62 | if (runnable == null)
63 | break;
64 | runnable.run();
65 | processed++;
66 | if (processed >= throughput)
67 | break;
68 | }
69 | int remaining = mailbox.queued.addAndGet(-processed);
70 | if (remaining > 0) {
71 | executor.execute(() -> processMailbox(mailbox));
72 | }
73 | }
74 |
75 | @Override
76 | public void close() {
77 | this.shutdown = true;
78 | executor.shutdown();
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/com/zakgof/actr/impl/FastRegSet.java:
--------------------------------------------------------------------------------
1 | package com.zakgof.actr.impl;
2 |
3 | import java.util.Collection;
4 |
5 | class FastRegSet implements IRegSet {
6 |
7 | private final ConcurrentDoublyLinkedList