O(1) lock free container The 2019 Stack Overflow Developer Survey Results Are InLock-Free Ring...

Carnot-Caratheodory metric

How to make payment on the internet without leaving a money trail?

How to change the limits of integration

The difference between dialogue marks

What does Linus Torvalds means when he says that git "never ever" tracks a file?

"What time...?" or "At what time...?" - what is more grammatically correct?

Geography at the pixel level

What is the best strategy for white in this position?

Why don't Unix/Linux systems traverse through directories until they find the required version of a linked library?

Why Did Howard Stark Use All The Vibranium They Had On A Prototype Shield?

How to create dashed lines/arrows in Illustrator

A poker game description that does not feel gimmicky

Should I write numbers in words or as numerals when there are multiple next to each other?

How to reverse every other sublist of a list?

Inflated grade on resume at previous job, might former employer tell new employer?

What do hard-Brexiteers want with respect to the Irish border?

How was Skylab's orbit inclination chosen?

What does "rabbited" mean/imply in this sentence?

How to manage monthly salary

How can I fix this gap between bookcases I made?

Monty Hall variation

Limit the amount of RAM Mathematica may access?

Pristine Bit Checking

It's possible to achieve negative score?



O(1) lock free container



The 2019 Stack Overflow Developer Survey Results Are InLock-Free Ring ImplementationLock-Free Ring ImplementationLock-free DoubleBufferedListLock-free multiple-consumer multiple-producer queueMax heap in Javalock-free job queue without size restriction (multiple read/write)Lock-free ringbuffer with multiple readers in C++11Implementation of a lock-free fixed-sized allocatorFast insert unique containerCustomized lock-free RingBufferLeetcode #146. LRUCache solution in Java (Doubly Linked List + HashMap)





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}







16












$begingroup$


This is a lock-free Container class. By Container I mean it will hold objects for iteration at a later time. To achieve $O(1)$ it returns a token from the put method which you can then use to remove the object. The token is actually the node in which the object has been placed.



It is instantiated with a fixed size and throws an exception if capacity is exhausted. Internally it uses a ring buffer.



It is not intended as a candidate for the Collections toolbox.



Please try to find a way to break it if you can, or point out any areas where you think it might fail under certain situations.



/**
* Container
* ---------
*
* A lock-free container that offers a close-to O(1) add/remove performance.
*
*/
public class Container<T> implements Iterable<T> {

// The capacity of the container.
final int capacity;
// The list.
AtomicReference<Node<T>> head = new AtomicReference<Node<T>>();

// Constructor
public Container(int capacity) {
this.capacity = capacity;
// Construct the list.
Node<T> h = new Node<T>();
Node<T> it = h;
// One created, now add (capacity - 1) more
for (int i = 0; i < capacity - 1; i++) {
// Add it.
it.next = new Node<T>();
// Step on to it.
it = it.next;
}
// Make it a ring.
it.next = h;
// Install it.
head.set(h);
}

// Empty ... NOT thread safe.
public void clear() {
Node<T> it = head.get();
for (int i = 0; i < capacity; i++) {
// Trash the element
it.element = null;
// Mark it free.
it.free.set(true);
it = it.next;
}
}

// Add a new one.
public Node<T> add(T element) {
// Get a free node and attach the element.
return getFree().attach(element);
}

// Find the next free element and mark it not free.
private Node<T> getFree() {
Node<T> freeNode = head.get();
int skipped = 0;
// Stop when we hit the end of the list
// ... or we successfully transit a node from free to not-free.
while (skipped < capacity && !freeNode.free.compareAndSet(true, false)) {
skipped += 1;
freeNode = freeNode.next;
}
if (skipped < capacity) {
// Put the head as next.
// Doesn't matter if it fails. That would just mean someone else was doing the same.
head.set(freeNode.next);
} else {
// We hit the end! No more free nodes.
throw new IllegalStateException("Capacity exhausted.");
}
return freeNode;
}

// Mark it free.
public void remove(Node<T> it, T element) {
// Remove the element first.
it.detach(element);
// Mark it as free.
if (!it.free.compareAndSet(false, true)) {
throw new IllegalStateException("Freeing a freed node.");
}
}

// The Node class. It is static so needs the <T> repeated.
public static class Node<T> {

// The element in the node.
private T element;
// Are we free?
private AtomicBoolean free = new AtomicBoolean(true);
// The next reference in whatever list I am in.
private Node<T> next;

// Construct a node of the list
private Node() {
// Start empty.
element = null;
}

// Attach the element.
public Node<T> attach(T element) {
// Sanity check.
if (this.element == null) {
this.element = element;
} else {
throw new IllegalArgumentException("There is already an element attached.");
}
// Useful for chaining.
return this;
}

// Detach the element.
public Node<T> detach(T element) {
// Sanity check.
if (this.element == element) {
this.element = null;
} else {
throw new IllegalArgumentException("Removal of wrong element.");
}
// Useful for chaining.
return this;
}

public T get () {
return element;
}

@Override
public String toString() {
return element != null ? element.toString() : "null";
}
}

// Provides an iterator across all items in the container.
@Override
public Iterator<T> iterator() {
return new UsedNodesIterator<T>(this);
}

// Iterates across used nodes.
private static class UsedNodesIterator<T> implements Iterator<T> {

// Where next to look for the next used node.
Node<T> it;
int limit = 0;
T next = null;

public UsedNodesIterator(Container<T> c) {
// Snapshot the head node at this time.
it = c.head.get();
limit = c.capacity;
}

@Override
public boolean hasNext() {
// Made into a `while` loop to fix issue reported by @Nim
while (next == null && limit > 0) {
// Scan to the next non-free node.
while (limit > 0 && it.free.get() == true) {
it = it.next;
// Step down 1.
limit -= 1;
}
if (limit != 0) {
next = it.element;
}
}
return next != null;
}

@Override
public T next() {
T n = null;
if ( hasNext () ) {
// Give it to them.
n = next;
next = null;
// Step forward.
it = it.next;
limit -= 1;
} else {
// Not there!!
throw new NoSuchElementException ();
}
return n;
}

@Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
}

}


Having posted this request I now find myself searching aggressively for areas of doubt in the code. Some of those follow but I plan to edit the code and remove these as and when you point me in the right direction.




  1. Should Node<T>.element be volatile? I think perhaps.

  2. Should Node<T>.next be volatile? I think not but I am not sure why.

  3. Have I chosen a good method for the iterator? I picked a counter rather than seeing the head for the second time to ensure I am resilient to an increase/decrease in size of the ring. This has the small downside of having slightly less predictability but it guarantees termination and is only a bit strange under high parallelism.


NB: Container is not intended to be used between a producer and a consumer. It is designed more for keeping a note of which objects have a particular feature in an iterable way. I use it to keep track of threads in the process of calling put on a BlockingQueue (which may of course block) so I can interrupt them if the queue needs to close.










share|improve this question











$endgroup$












  • $begingroup$
    The original post is here.
    $endgroup$
    – OldCurmudgeon
    Jun 18 '12 at 13:38










  • $begingroup$
    The declaration of stop variable seems to be missing.
    $endgroup$
    – rahul
    Jun 21 '12 at 16:26












  • $begingroup$
    @blufox - my apologies - it should have read limit
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 18:13


















16












$begingroup$


This is a lock-free Container class. By Container I mean it will hold objects for iteration at a later time. To achieve $O(1)$ it returns a token from the put method which you can then use to remove the object. The token is actually the node in which the object has been placed.



It is instantiated with a fixed size and throws an exception if capacity is exhausted. Internally it uses a ring buffer.



It is not intended as a candidate for the Collections toolbox.



Please try to find a way to break it if you can, or point out any areas where you think it might fail under certain situations.



/**
* Container
* ---------
*
* A lock-free container that offers a close-to O(1) add/remove performance.
*
*/
public class Container<T> implements Iterable<T> {

// The capacity of the container.
final int capacity;
// The list.
AtomicReference<Node<T>> head = new AtomicReference<Node<T>>();

// Constructor
public Container(int capacity) {
this.capacity = capacity;
// Construct the list.
Node<T> h = new Node<T>();
Node<T> it = h;
// One created, now add (capacity - 1) more
for (int i = 0; i < capacity - 1; i++) {
// Add it.
it.next = new Node<T>();
// Step on to it.
it = it.next;
}
// Make it a ring.
it.next = h;
// Install it.
head.set(h);
}

// Empty ... NOT thread safe.
public void clear() {
Node<T> it = head.get();
for (int i = 0; i < capacity; i++) {
// Trash the element
it.element = null;
// Mark it free.
it.free.set(true);
it = it.next;
}
}

// Add a new one.
public Node<T> add(T element) {
// Get a free node and attach the element.
return getFree().attach(element);
}

// Find the next free element and mark it not free.
private Node<T> getFree() {
Node<T> freeNode = head.get();
int skipped = 0;
// Stop when we hit the end of the list
// ... or we successfully transit a node from free to not-free.
while (skipped < capacity && !freeNode.free.compareAndSet(true, false)) {
skipped += 1;
freeNode = freeNode.next;
}
if (skipped < capacity) {
// Put the head as next.
// Doesn't matter if it fails. That would just mean someone else was doing the same.
head.set(freeNode.next);
} else {
// We hit the end! No more free nodes.
throw new IllegalStateException("Capacity exhausted.");
}
return freeNode;
}

// Mark it free.
public void remove(Node<T> it, T element) {
// Remove the element first.
it.detach(element);
// Mark it as free.
if (!it.free.compareAndSet(false, true)) {
throw new IllegalStateException("Freeing a freed node.");
}
}

// The Node class. It is static so needs the <T> repeated.
public static class Node<T> {

// The element in the node.
private T element;
// Are we free?
private AtomicBoolean free = new AtomicBoolean(true);
// The next reference in whatever list I am in.
private Node<T> next;

// Construct a node of the list
private Node() {
// Start empty.
element = null;
}

// Attach the element.
public Node<T> attach(T element) {
// Sanity check.
if (this.element == null) {
this.element = element;
} else {
throw new IllegalArgumentException("There is already an element attached.");
}
// Useful for chaining.
return this;
}

// Detach the element.
public Node<T> detach(T element) {
// Sanity check.
if (this.element == element) {
this.element = null;
} else {
throw new IllegalArgumentException("Removal of wrong element.");
}
// Useful for chaining.
return this;
}

public T get () {
return element;
}

@Override
public String toString() {
return element != null ? element.toString() : "null";
}
}

// Provides an iterator across all items in the container.
@Override
public Iterator<T> iterator() {
return new UsedNodesIterator<T>(this);
}

// Iterates across used nodes.
private static class UsedNodesIterator<T> implements Iterator<T> {

// Where next to look for the next used node.
Node<T> it;
int limit = 0;
T next = null;

public UsedNodesIterator(Container<T> c) {
// Snapshot the head node at this time.
it = c.head.get();
limit = c.capacity;
}

@Override
public boolean hasNext() {
// Made into a `while` loop to fix issue reported by @Nim
while (next == null && limit > 0) {
// Scan to the next non-free node.
while (limit > 0 && it.free.get() == true) {
it = it.next;
// Step down 1.
limit -= 1;
}
if (limit != 0) {
next = it.element;
}
}
return next != null;
}

@Override
public T next() {
T n = null;
if ( hasNext () ) {
// Give it to them.
n = next;
next = null;
// Step forward.
it = it.next;
limit -= 1;
} else {
// Not there!!
throw new NoSuchElementException ();
}
return n;
}

@Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
}

}


Having posted this request I now find myself searching aggressively for areas of doubt in the code. Some of those follow but I plan to edit the code and remove these as and when you point me in the right direction.




  1. Should Node<T>.element be volatile? I think perhaps.

  2. Should Node<T>.next be volatile? I think not but I am not sure why.

  3. Have I chosen a good method for the iterator? I picked a counter rather than seeing the head for the second time to ensure I am resilient to an increase/decrease in size of the ring. This has the small downside of having slightly less predictability but it guarantees termination and is only a bit strange under high parallelism.


NB: Container is not intended to be used between a producer and a consumer. It is designed more for keeping a note of which objects have a particular feature in an iterable way. I use it to keep track of threads in the process of calling put on a BlockingQueue (which may of course block) so I can interrupt them if the queue needs to close.










share|improve this question











$endgroup$












  • $begingroup$
    The original post is here.
    $endgroup$
    – OldCurmudgeon
    Jun 18 '12 at 13:38










  • $begingroup$
    The declaration of stop variable seems to be missing.
    $endgroup$
    – rahul
    Jun 21 '12 at 16:26












  • $begingroup$
    @blufox - my apologies - it should have read limit
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 18:13














16












16








16


5



$begingroup$


This is a lock-free Container class. By Container I mean it will hold objects for iteration at a later time. To achieve $O(1)$ it returns a token from the put method which you can then use to remove the object. The token is actually the node in which the object has been placed.



It is instantiated with a fixed size and throws an exception if capacity is exhausted. Internally it uses a ring buffer.



It is not intended as a candidate for the Collections toolbox.



Please try to find a way to break it if you can, or point out any areas where you think it might fail under certain situations.



/**
* Container
* ---------
*
* A lock-free container that offers a close-to O(1) add/remove performance.
*
*/
public class Container<T> implements Iterable<T> {

// The capacity of the container.
final int capacity;
// The list.
AtomicReference<Node<T>> head = new AtomicReference<Node<T>>();

// Constructor
public Container(int capacity) {
this.capacity = capacity;
// Construct the list.
Node<T> h = new Node<T>();
Node<T> it = h;
// One created, now add (capacity - 1) more
for (int i = 0; i < capacity - 1; i++) {
// Add it.
it.next = new Node<T>();
// Step on to it.
it = it.next;
}
// Make it a ring.
it.next = h;
// Install it.
head.set(h);
}

// Empty ... NOT thread safe.
public void clear() {
Node<T> it = head.get();
for (int i = 0; i < capacity; i++) {
// Trash the element
it.element = null;
// Mark it free.
it.free.set(true);
it = it.next;
}
}

// Add a new one.
public Node<T> add(T element) {
// Get a free node and attach the element.
return getFree().attach(element);
}

// Find the next free element and mark it not free.
private Node<T> getFree() {
Node<T> freeNode = head.get();
int skipped = 0;
// Stop when we hit the end of the list
// ... or we successfully transit a node from free to not-free.
while (skipped < capacity && !freeNode.free.compareAndSet(true, false)) {
skipped += 1;
freeNode = freeNode.next;
}
if (skipped < capacity) {
// Put the head as next.
// Doesn't matter if it fails. That would just mean someone else was doing the same.
head.set(freeNode.next);
} else {
// We hit the end! No more free nodes.
throw new IllegalStateException("Capacity exhausted.");
}
return freeNode;
}

// Mark it free.
public void remove(Node<T> it, T element) {
// Remove the element first.
it.detach(element);
// Mark it as free.
if (!it.free.compareAndSet(false, true)) {
throw new IllegalStateException("Freeing a freed node.");
}
}

// The Node class. It is static so needs the <T> repeated.
public static class Node<T> {

// The element in the node.
private T element;
// Are we free?
private AtomicBoolean free = new AtomicBoolean(true);
// The next reference in whatever list I am in.
private Node<T> next;

// Construct a node of the list
private Node() {
// Start empty.
element = null;
}

// Attach the element.
public Node<T> attach(T element) {
// Sanity check.
if (this.element == null) {
this.element = element;
} else {
throw new IllegalArgumentException("There is already an element attached.");
}
// Useful for chaining.
return this;
}

// Detach the element.
public Node<T> detach(T element) {
// Sanity check.
if (this.element == element) {
this.element = null;
} else {
throw new IllegalArgumentException("Removal of wrong element.");
}
// Useful for chaining.
return this;
}

public T get () {
return element;
}

@Override
public String toString() {
return element != null ? element.toString() : "null";
}
}

// Provides an iterator across all items in the container.
@Override
public Iterator<T> iterator() {
return new UsedNodesIterator<T>(this);
}

// Iterates across used nodes.
private static class UsedNodesIterator<T> implements Iterator<T> {

// Where next to look for the next used node.
Node<T> it;
int limit = 0;
T next = null;

public UsedNodesIterator(Container<T> c) {
// Snapshot the head node at this time.
it = c.head.get();
limit = c.capacity;
}

@Override
public boolean hasNext() {
// Made into a `while` loop to fix issue reported by @Nim
while (next == null && limit > 0) {
// Scan to the next non-free node.
while (limit > 0 && it.free.get() == true) {
it = it.next;
// Step down 1.
limit -= 1;
}
if (limit != 0) {
next = it.element;
}
}
return next != null;
}

@Override
public T next() {
T n = null;
if ( hasNext () ) {
// Give it to them.
n = next;
next = null;
// Step forward.
it = it.next;
limit -= 1;
} else {
// Not there!!
throw new NoSuchElementException ();
}
return n;
}

@Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
}

}


Having posted this request I now find myself searching aggressively for areas of doubt in the code. Some of those follow but I plan to edit the code and remove these as and when you point me in the right direction.




  1. Should Node<T>.element be volatile? I think perhaps.

  2. Should Node<T>.next be volatile? I think not but I am not sure why.

  3. Have I chosen a good method for the iterator? I picked a counter rather than seeing the head for the second time to ensure I am resilient to an increase/decrease in size of the ring. This has the small downside of having slightly less predictability but it guarantees termination and is only a bit strange under high parallelism.


NB: Container is not intended to be used between a producer and a consumer. It is designed more for keeping a note of which objects have a particular feature in an iterable way. I use it to keep track of threads in the process of calling put on a BlockingQueue (which may of course block) so I can interrupt them if the queue needs to close.










share|improve this question











$endgroup$




This is a lock-free Container class. By Container I mean it will hold objects for iteration at a later time. To achieve $O(1)$ it returns a token from the put method which you can then use to remove the object. The token is actually the node in which the object has been placed.



It is instantiated with a fixed size and throws an exception if capacity is exhausted. Internally it uses a ring buffer.



It is not intended as a candidate for the Collections toolbox.



Please try to find a way to break it if you can, or point out any areas where you think it might fail under certain situations.



/**
* Container
* ---------
*
* A lock-free container that offers a close-to O(1) add/remove performance.
*
*/
public class Container<T> implements Iterable<T> {

// The capacity of the container.
final int capacity;
// The list.
AtomicReference<Node<T>> head = new AtomicReference<Node<T>>();

// Constructor
public Container(int capacity) {
this.capacity = capacity;
// Construct the list.
Node<T> h = new Node<T>();
Node<T> it = h;
// One created, now add (capacity - 1) more
for (int i = 0; i < capacity - 1; i++) {
// Add it.
it.next = new Node<T>();
// Step on to it.
it = it.next;
}
// Make it a ring.
it.next = h;
// Install it.
head.set(h);
}

// Empty ... NOT thread safe.
public void clear() {
Node<T> it = head.get();
for (int i = 0; i < capacity; i++) {
// Trash the element
it.element = null;
// Mark it free.
it.free.set(true);
it = it.next;
}
}

// Add a new one.
public Node<T> add(T element) {
// Get a free node and attach the element.
return getFree().attach(element);
}

// Find the next free element and mark it not free.
private Node<T> getFree() {
Node<T> freeNode = head.get();
int skipped = 0;
// Stop when we hit the end of the list
// ... or we successfully transit a node from free to not-free.
while (skipped < capacity && !freeNode.free.compareAndSet(true, false)) {
skipped += 1;
freeNode = freeNode.next;
}
if (skipped < capacity) {
// Put the head as next.
// Doesn't matter if it fails. That would just mean someone else was doing the same.
head.set(freeNode.next);
} else {
// We hit the end! No more free nodes.
throw new IllegalStateException("Capacity exhausted.");
}
return freeNode;
}

// Mark it free.
public void remove(Node<T> it, T element) {
// Remove the element first.
it.detach(element);
// Mark it as free.
if (!it.free.compareAndSet(false, true)) {
throw new IllegalStateException("Freeing a freed node.");
}
}

// The Node class. It is static so needs the <T> repeated.
public static class Node<T> {

// The element in the node.
private T element;
// Are we free?
private AtomicBoolean free = new AtomicBoolean(true);
// The next reference in whatever list I am in.
private Node<T> next;

// Construct a node of the list
private Node() {
// Start empty.
element = null;
}

// Attach the element.
public Node<T> attach(T element) {
// Sanity check.
if (this.element == null) {
this.element = element;
} else {
throw new IllegalArgumentException("There is already an element attached.");
}
// Useful for chaining.
return this;
}

// Detach the element.
public Node<T> detach(T element) {
// Sanity check.
if (this.element == element) {
this.element = null;
} else {
throw new IllegalArgumentException("Removal of wrong element.");
}
// Useful for chaining.
return this;
}

public T get () {
return element;
}

@Override
public String toString() {
return element != null ? element.toString() : "null";
}
}

// Provides an iterator across all items in the container.
@Override
public Iterator<T> iterator() {
return new UsedNodesIterator<T>(this);
}

// Iterates across used nodes.
private static class UsedNodesIterator<T> implements Iterator<T> {

// Where next to look for the next used node.
Node<T> it;
int limit = 0;
T next = null;

public UsedNodesIterator(Container<T> c) {
// Snapshot the head node at this time.
it = c.head.get();
limit = c.capacity;
}

@Override
public boolean hasNext() {
// Made into a `while` loop to fix issue reported by @Nim
while (next == null && limit > 0) {
// Scan to the next non-free node.
while (limit > 0 && it.free.get() == true) {
it = it.next;
// Step down 1.
limit -= 1;
}
if (limit != 0) {
next = it.element;
}
}
return next != null;
}

@Override
public T next() {
T n = null;
if ( hasNext () ) {
// Give it to them.
n = next;
next = null;
// Step forward.
it = it.next;
limit -= 1;
} else {
// Not there!!
throw new NoSuchElementException ();
}
return n;
}

@Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
}

}


Having posted this request I now find myself searching aggressively for areas of doubt in the code. Some of those follow but I plan to edit the code and remove these as and when you point me in the right direction.




  1. Should Node<T>.element be volatile? I think perhaps.

  2. Should Node<T>.next be volatile? I think not but I am not sure why.

  3. Have I chosen a good method for the iterator? I picked a counter rather than seeing the head for the second time to ensure I am resilient to an increase/decrease in size of the ring. This has the small downside of having slightly less predictability but it guarantees termination and is only a bit strange under high parallelism.


NB: Container is not intended to be used between a producer and a consumer. It is designed more for keeping a note of which objects have a particular feature in an iterable way. I use it to keep track of threads in the process of calling put on a BlockingQueue (which may of course block) so I can interrupt them if the queue needs to close.







java collections circular-list lock-free atomic






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Apr 6 '15 at 1:33









Jamal

30.6k11121227




30.6k11121227










asked Jun 18 '12 at 13:36









OldCurmudgeonOldCurmudgeon

1,49821527




1,49821527












  • $begingroup$
    The original post is here.
    $endgroup$
    – OldCurmudgeon
    Jun 18 '12 at 13:38










  • $begingroup$
    The declaration of stop variable seems to be missing.
    $endgroup$
    – rahul
    Jun 21 '12 at 16:26












  • $begingroup$
    @blufox - my apologies - it should have read limit
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 18:13


















  • $begingroup$
    The original post is here.
    $endgroup$
    – OldCurmudgeon
    Jun 18 '12 at 13:38










  • $begingroup$
    The declaration of stop variable seems to be missing.
    $endgroup$
    – rahul
    Jun 21 '12 at 16:26












  • $begingroup$
    @blufox - my apologies - it should have read limit
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 18:13
















$begingroup$
The original post is here.
$endgroup$
– OldCurmudgeon
Jun 18 '12 at 13:38




$begingroup$
The original post is here.
$endgroup$
– OldCurmudgeon
Jun 18 '12 at 13:38












$begingroup$
The declaration of stop variable seems to be missing.
$endgroup$
– rahul
Jun 21 '12 at 16:26






$begingroup$
The declaration of stop variable seems to be missing.
$endgroup$
– rahul
Jun 21 '12 at 16:26














$begingroup$
@blufox - my apologies - it should have read limit
$endgroup$
– OldCurmudgeon
Jun 21 '12 at 18:13




$begingroup$
@blufox - my apologies - it should have read limit
$endgroup$
– OldCurmudgeon
Jun 21 '12 at 18:13










2 Answers
2






active

oldest

votes


















6





+50







$begingroup$

I thought about a comment, but too long, several things I can think of:




  1. This does not guarantee order, i.e. an item is entered into the next free slot, and if there is a fast producer, the order the consumer sees is not guaranteed. Is this intentional?


  2. There is no atomic combined detach/attach operation, this means that for example:
    Thread A: Attach starts, calls getFree(), which marks the node as not free (and is switched out before the element reference is set)
    Thread B: is iterating, and hits this node, sees it's not free, so valid node, and returns this, if access to element is done (without a null pointer check), will result in an exception - I guess this is okay(?) Or should you test in your iterator and throw a ConcurrentModification?



I think it's simpler if you maintain an AtomicReference in the node to the element and test that rather than a separate boolean and the reference.



Edit 1:



Looking at this:



  // Doesn't matter if it fails. That would just mean someone else was doing the same.
head.set(freeNode.next);


Is this necessarily correct? If Thread A sets $n+1$ as head at the same time as Thread B setting n as head, and B succeeds - is the ring in a consistent state? I guess the no ordering guarantee means that this doesn't matter too much, but it would violate the $O(1)$ claim! ;) In fact, I would question the whole $O(1)$ claim, this is at best $O(1)$ and at worst $O(n)$...






share|improve this answer











$endgroup$













  • $begingroup$
    1. You are correct - no statement has been made about ordering and there are no guarantees in that respect. All that is guaranteed is that anything that is in the Container throughout iteration time will be offered to the Iterator user. Anything that is added/removed to/from the Container during iteration may or may not be presented to the user.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:06






  • 1




    $begingroup$
    2. I see your concern. Very good catch. This scenario could then return false from hasNext and thus halt the iteration prematurely. I will fix that. A combined attach/detach would be an interesting detail. Note that the Node class is public and exposes its attach and detach methods so it is possible (encouraged) to replace the object without freeing the slot it occupies if that is desired.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:07










  • $begingroup$
    I hold the AtomicBoolean free in the Node to allow for the above activity (instant detach/attach). I will investigate your idea to see how it affects the algorithm.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:11










  • $begingroup$
    2. (revisited) On further thought, your scenario may result in null being returned by the next method but will not result in a breach of contract as the iteration will continue because next is a Node not an element. My mistake. BTW - there is an even wider window for this issue because the element can be removed any time between hasNext and next.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:18






  • 3




    $begingroup$
    btw - I think the standard practice with Iterator is that you would call hasNext() and if this is true, call next() to get the element, you should not invoke hasNext() within the call to next(), next() should just return the element.
    $endgroup$
    – Nim
    Jun 21 '12 at 14:37





















0












$begingroup$

If thread A does Container.remove() and then gets interrupted by thread B after the .detach() but before the //Mark it free, and thread B then iterates, it's going to get a ref to an item that's null, which is probably Not What You Want.






share|improve this answer









$endgroup$













  • $begingroup$
    Thanks for the feedback - That was the case in the original code but a recent change triggered by a similar scenario pointed out by @Nim means that the iterator will never return a null value. In your case the null value will be skipped.
    $endgroup$
    – OldCurmudgeon
    Jun 22 '12 at 8:15












Your Answer





StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");

StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f12691%2fo1-lock-free-container%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









6





+50







$begingroup$

I thought about a comment, but too long, several things I can think of:




  1. This does not guarantee order, i.e. an item is entered into the next free slot, and if there is a fast producer, the order the consumer sees is not guaranteed. Is this intentional?


  2. There is no atomic combined detach/attach operation, this means that for example:
    Thread A: Attach starts, calls getFree(), which marks the node as not free (and is switched out before the element reference is set)
    Thread B: is iterating, and hits this node, sees it's not free, so valid node, and returns this, if access to element is done (without a null pointer check), will result in an exception - I guess this is okay(?) Or should you test in your iterator and throw a ConcurrentModification?



I think it's simpler if you maintain an AtomicReference in the node to the element and test that rather than a separate boolean and the reference.



Edit 1:



Looking at this:



  // Doesn't matter if it fails. That would just mean someone else was doing the same.
head.set(freeNode.next);


Is this necessarily correct? If Thread A sets $n+1$ as head at the same time as Thread B setting n as head, and B succeeds - is the ring in a consistent state? I guess the no ordering guarantee means that this doesn't matter too much, but it would violate the $O(1)$ claim! ;) In fact, I would question the whole $O(1)$ claim, this is at best $O(1)$ and at worst $O(n)$...






share|improve this answer











$endgroup$













  • $begingroup$
    1. You are correct - no statement has been made about ordering and there are no guarantees in that respect. All that is guaranteed is that anything that is in the Container throughout iteration time will be offered to the Iterator user. Anything that is added/removed to/from the Container during iteration may or may not be presented to the user.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:06






  • 1




    $begingroup$
    2. I see your concern. Very good catch. This scenario could then return false from hasNext and thus halt the iteration prematurely. I will fix that. A combined attach/detach would be an interesting detail. Note that the Node class is public and exposes its attach and detach methods so it is possible (encouraged) to replace the object without freeing the slot it occupies if that is desired.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:07










  • $begingroup$
    I hold the AtomicBoolean free in the Node to allow for the above activity (instant detach/attach). I will investigate your idea to see how it affects the algorithm.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:11










  • $begingroup$
    2. (revisited) On further thought, your scenario may result in null being returned by the next method but will not result in a breach of contract as the iteration will continue because next is a Node not an element. My mistake. BTW - there is an even wider window for this issue because the element can be removed any time between hasNext and next.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:18






  • 3




    $begingroup$
    btw - I think the standard practice with Iterator is that you would call hasNext() and if this is true, call next() to get the element, you should not invoke hasNext() within the call to next(), next() should just return the element.
    $endgroup$
    – Nim
    Jun 21 '12 at 14:37


















6





+50







$begingroup$

I thought about a comment, but too long, several things I can think of:




  1. This does not guarantee order, i.e. an item is entered into the next free slot, and if there is a fast producer, the order the consumer sees is not guaranteed. Is this intentional?


  2. There is no atomic combined detach/attach operation, this means that for example:
    Thread A: Attach starts, calls getFree(), which marks the node as not free (and is switched out before the element reference is set)
    Thread B: is iterating, and hits this node, sees it's not free, so valid node, and returns this, if access to element is done (without a null pointer check), will result in an exception - I guess this is okay(?) Or should you test in your iterator and throw a ConcurrentModification?



I think it's simpler if you maintain an AtomicReference in the node to the element and test that rather than a separate boolean and the reference.



Edit 1:



Looking at this:



  // Doesn't matter if it fails. That would just mean someone else was doing the same.
head.set(freeNode.next);


Is this necessarily correct? If Thread A sets $n+1$ as head at the same time as Thread B setting n as head, and B succeeds - is the ring in a consistent state? I guess the no ordering guarantee means that this doesn't matter too much, but it would violate the $O(1)$ claim! ;) In fact, I would question the whole $O(1)$ claim, this is at best $O(1)$ and at worst $O(n)$...






share|improve this answer











$endgroup$













  • $begingroup$
    1. You are correct - no statement has been made about ordering and there are no guarantees in that respect. All that is guaranteed is that anything that is in the Container throughout iteration time will be offered to the Iterator user. Anything that is added/removed to/from the Container during iteration may or may not be presented to the user.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:06






  • 1




    $begingroup$
    2. I see your concern. Very good catch. This scenario could then return false from hasNext and thus halt the iteration prematurely. I will fix that. A combined attach/detach would be an interesting detail. Note that the Node class is public and exposes its attach and detach methods so it is possible (encouraged) to replace the object without freeing the slot it occupies if that is desired.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:07










  • $begingroup$
    I hold the AtomicBoolean free in the Node to allow for the above activity (instant detach/attach). I will investigate your idea to see how it affects the algorithm.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:11










  • $begingroup$
    2. (revisited) On further thought, your scenario may result in null being returned by the next method but will not result in a breach of contract as the iteration will continue because next is a Node not an element. My mistake. BTW - there is an even wider window for this issue because the element can be removed any time between hasNext and next.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:18






  • 3




    $begingroup$
    btw - I think the standard practice with Iterator is that you would call hasNext() and if this is true, call next() to get the element, you should not invoke hasNext() within the call to next(), next() should just return the element.
    $endgroup$
    – Nim
    Jun 21 '12 at 14:37
















6





+50







6





+50



6




+50



$begingroup$

I thought about a comment, but too long, several things I can think of:




  1. This does not guarantee order, i.e. an item is entered into the next free slot, and if there is a fast producer, the order the consumer sees is not guaranteed. Is this intentional?


  2. There is no atomic combined detach/attach operation, this means that for example:
    Thread A: Attach starts, calls getFree(), which marks the node as not free (and is switched out before the element reference is set)
    Thread B: is iterating, and hits this node, sees it's not free, so valid node, and returns this, if access to element is done (without a null pointer check), will result in an exception - I guess this is okay(?) Or should you test in your iterator and throw a ConcurrentModification?



I think it's simpler if you maintain an AtomicReference in the node to the element and test that rather than a separate boolean and the reference.



Edit 1:



Looking at this:



  // Doesn't matter if it fails. That would just mean someone else was doing the same.
head.set(freeNode.next);


Is this necessarily correct? If Thread A sets $n+1$ as head at the same time as Thread B setting n as head, and B succeeds - is the ring in a consistent state? I guess the no ordering guarantee means that this doesn't matter too much, but it would violate the $O(1)$ claim! ;) In fact, I would question the whole $O(1)$ claim, this is at best $O(1)$ and at worst $O(n)$...






share|improve this answer











$endgroup$



I thought about a comment, but too long, several things I can think of:




  1. This does not guarantee order, i.e. an item is entered into the next free slot, and if there is a fast producer, the order the consumer sees is not guaranteed. Is this intentional?


  2. There is no atomic combined detach/attach operation, this means that for example:
    Thread A: Attach starts, calls getFree(), which marks the node as not free (and is switched out before the element reference is set)
    Thread B: is iterating, and hits this node, sees it's not free, so valid node, and returns this, if access to element is done (without a null pointer check), will result in an exception - I guess this is okay(?) Or should you test in your iterator and throw a ConcurrentModification?



I think it's simpler if you maintain an AtomicReference in the node to the element and test that rather than a separate boolean and the reference.



Edit 1:



Looking at this:



  // Doesn't matter if it fails. That would just mean someone else was doing the same.
head.set(freeNode.next);


Is this necessarily correct? If Thread A sets $n+1$ as head at the same time as Thread B setting n as head, and B succeeds - is the ring in a consistent state? I guess the no ordering guarantee means that this doesn't matter too much, but it would violate the $O(1)$ claim! ;) In fact, I would question the whole $O(1)$ claim, this is at best $O(1)$ and at worst $O(n)$...







share|improve this answer














share|improve this answer



share|improve this answer








edited 4 hours ago









esote

3,02611241




3,02611241










answered Jun 21 '12 at 13:03









NimNim

375139




375139












  • $begingroup$
    1. You are correct - no statement has been made about ordering and there are no guarantees in that respect. All that is guaranteed is that anything that is in the Container throughout iteration time will be offered to the Iterator user. Anything that is added/removed to/from the Container during iteration may or may not be presented to the user.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:06






  • 1




    $begingroup$
    2. I see your concern. Very good catch. This scenario could then return false from hasNext and thus halt the iteration prematurely. I will fix that. A combined attach/detach would be an interesting detail. Note that the Node class is public and exposes its attach and detach methods so it is possible (encouraged) to replace the object without freeing the slot it occupies if that is desired.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:07










  • $begingroup$
    I hold the AtomicBoolean free in the Node to allow for the above activity (instant detach/attach). I will investigate your idea to see how it affects the algorithm.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:11










  • $begingroup$
    2. (revisited) On further thought, your scenario may result in null being returned by the next method but will not result in a breach of contract as the iteration will continue because next is a Node not an element. My mistake. BTW - there is an even wider window for this issue because the element can be removed any time between hasNext and next.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:18






  • 3




    $begingroup$
    btw - I think the standard practice with Iterator is that you would call hasNext() and if this is true, call next() to get the element, you should not invoke hasNext() within the call to next(), next() should just return the element.
    $endgroup$
    – Nim
    Jun 21 '12 at 14:37




















  • $begingroup$
    1. You are correct - no statement has been made about ordering and there are no guarantees in that respect. All that is guaranteed is that anything that is in the Container throughout iteration time will be offered to the Iterator user. Anything that is added/removed to/from the Container during iteration may or may not be presented to the user.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:06






  • 1




    $begingroup$
    2. I see your concern. Very good catch. This scenario could then return false from hasNext and thus halt the iteration prematurely. I will fix that. A combined attach/detach would be an interesting detail. Note that the Node class is public and exposes its attach and detach methods so it is possible (encouraged) to replace the object without freeing the slot it occupies if that is desired.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:07










  • $begingroup$
    I hold the AtomicBoolean free in the Node to allow for the above activity (instant detach/attach). I will investigate your idea to see how it affects the algorithm.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:11










  • $begingroup$
    2. (revisited) On further thought, your scenario may result in null being returned by the next method but will not result in a breach of contract as the iteration will continue because next is a Node not an element. My mistake. BTW - there is an even wider window for this issue because the element can be removed any time between hasNext and next.
    $endgroup$
    – OldCurmudgeon
    Jun 21 '12 at 14:18






  • 3




    $begingroup$
    btw - I think the standard practice with Iterator is that you would call hasNext() and if this is true, call next() to get the element, you should not invoke hasNext() within the call to next(), next() should just return the element.
    $endgroup$
    – Nim
    Jun 21 '12 at 14:37


















$begingroup$
1. You are correct - no statement has been made about ordering and there are no guarantees in that respect. All that is guaranteed is that anything that is in the Container throughout iteration time will be offered to the Iterator user. Anything that is added/removed to/from the Container during iteration may or may not be presented to the user.
$endgroup$
– OldCurmudgeon
Jun 21 '12 at 14:06




$begingroup$
1. You are correct - no statement has been made about ordering and there are no guarantees in that respect. All that is guaranteed is that anything that is in the Container throughout iteration time will be offered to the Iterator user. Anything that is added/removed to/from the Container during iteration may or may not be presented to the user.
$endgroup$
– OldCurmudgeon
Jun 21 '12 at 14:06




1




1




$begingroup$
2. I see your concern. Very good catch. This scenario could then return false from hasNext and thus halt the iteration prematurely. I will fix that. A combined attach/detach would be an interesting detail. Note that the Node class is public and exposes its attach and detach methods so it is possible (encouraged) to replace the object without freeing the slot it occupies if that is desired.
$endgroup$
– OldCurmudgeon
Jun 21 '12 at 14:07




$begingroup$
2. I see your concern. Very good catch. This scenario could then return false from hasNext and thus halt the iteration prematurely. I will fix that. A combined attach/detach would be an interesting detail. Note that the Node class is public and exposes its attach and detach methods so it is possible (encouraged) to replace the object without freeing the slot it occupies if that is desired.
$endgroup$
– OldCurmudgeon
Jun 21 '12 at 14:07












$begingroup$
I hold the AtomicBoolean free in the Node to allow for the above activity (instant detach/attach). I will investigate your idea to see how it affects the algorithm.
$endgroup$
– OldCurmudgeon
Jun 21 '12 at 14:11




$begingroup$
I hold the AtomicBoolean free in the Node to allow for the above activity (instant detach/attach). I will investigate your idea to see how it affects the algorithm.
$endgroup$
– OldCurmudgeon
Jun 21 '12 at 14:11












$begingroup$
2. (revisited) On further thought, your scenario may result in null being returned by the next method but will not result in a breach of contract as the iteration will continue because next is a Node not an element. My mistake. BTW - there is an even wider window for this issue because the element can be removed any time between hasNext and next.
$endgroup$
– OldCurmudgeon
Jun 21 '12 at 14:18




$begingroup$
2. (revisited) On further thought, your scenario may result in null being returned by the next method but will not result in a breach of contract as the iteration will continue because next is a Node not an element. My mistake. BTW - there is an even wider window for this issue because the element can be removed any time between hasNext and next.
$endgroup$
– OldCurmudgeon
Jun 21 '12 at 14:18




3




3




$begingroup$
btw - I think the standard practice with Iterator is that you would call hasNext() and if this is true, call next() to get the element, you should not invoke hasNext() within the call to next(), next() should just return the element.
$endgroup$
– Nim
Jun 21 '12 at 14:37






$begingroup$
btw - I think the standard practice with Iterator is that you would call hasNext() and if this is true, call next() to get the element, you should not invoke hasNext() within the call to next(), next() should just return the element.
$endgroup$
– Nim
Jun 21 '12 at 14:37















0












$begingroup$

If thread A does Container.remove() and then gets interrupted by thread B after the .detach() but before the //Mark it free, and thread B then iterates, it's going to get a ref to an item that's null, which is probably Not What You Want.






share|improve this answer









$endgroup$













  • $begingroup$
    Thanks for the feedback - That was the case in the original code but a recent change triggered by a similar scenario pointed out by @Nim means that the iterator will never return a null value. In your case the null value will be skipped.
    $endgroup$
    – OldCurmudgeon
    Jun 22 '12 at 8:15
















0












$begingroup$

If thread A does Container.remove() and then gets interrupted by thread B after the .detach() but before the //Mark it free, and thread B then iterates, it's going to get a ref to an item that's null, which is probably Not What You Want.






share|improve this answer









$endgroup$













  • $begingroup$
    Thanks for the feedback - That was the case in the original code but a recent change triggered by a similar scenario pointed out by @Nim means that the iterator will never return a null value. In your case the null value will be skipped.
    $endgroup$
    – OldCurmudgeon
    Jun 22 '12 at 8:15














0












0








0





$begingroup$

If thread A does Container.remove() and then gets interrupted by thread B after the .detach() but before the //Mark it free, and thread B then iterates, it's going to get a ref to an item that's null, which is probably Not What You Want.






share|improve this answer









$endgroup$



If thread A does Container.remove() and then gets interrupted by thread B after the .detach() but before the //Mark it free, and thread B then iterates, it's going to get a ref to an item that's null, which is probably Not What You Want.







share|improve this answer












share|improve this answer



share|improve this answer










answered Jun 22 '12 at 2:48









pjzpjz

2,191815




2,191815












  • $begingroup$
    Thanks for the feedback - That was the case in the original code but a recent change triggered by a similar scenario pointed out by @Nim means that the iterator will never return a null value. In your case the null value will be skipped.
    $endgroup$
    – OldCurmudgeon
    Jun 22 '12 at 8:15


















  • $begingroup$
    Thanks for the feedback - That was the case in the original code but a recent change triggered by a similar scenario pointed out by @Nim means that the iterator will never return a null value. In your case the null value will be skipped.
    $endgroup$
    – OldCurmudgeon
    Jun 22 '12 at 8:15
















$begingroup$
Thanks for the feedback - That was the case in the original code but a recent change triggered by a similar scenario pointed out by @Nim means that the iterator will never return a null value. In your case the null value will be skipped.
$endgroup$
– OldCurmudgeon
Jun 22 '12 at 8:15




$begingroup$
Thanks for the feedback - That was the case in the original code but a recent change triggered by a similar scenario pointed out by @Nim means that the iterator will never return a null value. In your case the null value will be skipped.
$endgroup$
– OldCurmudgeon
Jun 22 '12 at 8:15


















draft saved

draft discarded




















































Thanks for contributing an answer to Code Review Stack Exchange!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


Use MathJax to format equations. MathJax reference.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f12691%2fo1-lock-free-container%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

is 'sed' thread safeWhat should someone know about using Python scripts in the shell?Nexenta bash script uses...

How do i solve the “ No module named 'mlxtend' ” issue on Jupyter?

Pilgersdorf Inhaltsverzeichnis Geografie | Geschichte | Bevölkerungsentwicklung | Politik | Kultur...