he following is the public interface of the NonBlockingQueue.
Lines (5,6): The purpose of Node and Element is described in the general
design section (
Line (12): We do not insert Data. Instead, we insert the Node object that has
reference to a Data. Such design allows for optimizations and reduces the
burden of exception safety. The push and pop operations do not allocate or
release heap memory blocks. The "volatile" keyword is present in agreement
with the principles (
Line (14): This is a thread-safe extraction of some Node. There is no
deterministic order of extraction.
Line (15): This is a debugging feature.
It provides representation of NonBlockingQueue but does no global locking.
Hence, it creates such representation while NonBlockingQueue may be modified.
The argument DataToString provides a way to serialize the Data. The regular
"<<"-based approach is not applicable because some mutex-wise locking of
Data has to be involved. The user has to be mindful of the order of locking.
The regular use is this: first, lock Data, then do something with it including
queue operations that include Element lock and Node lock. The toString
operation has to do the opposite: lock Element, lock Node and then call
DataToString which includes Data lock. Such operation, if done naively, will
eventually deadlock. Therefore, the DataToString has to do a try-lock inside.
This is the reason it returns a pair. If the try-lock fails then the user code
may choose to return (false,"whatever"). If this is the case, then the
toString code releases Node and Element, waits a short while and attempts
again from the same position in the double linked list.
01\ template <class Data>
NonBlockingQueue : boost::noncopyable
push( volatile Node& data ) volatile;
Node& pop() volatile;