The following section documents the RCU API that is exposed in XRCU. It's used internally quite a bit by other parts of the library, yet it's very useful by itself when implementing other concurrent algorithms or data structures.
@@ -293,11 +151,11 @@
RCU finalizable objects
As it was mentioned before, XRCU defines a type called finalizable that is specifically designed to make its destruction safe (i.e: Only once all threads are outside a critical section). This type defines the following interface:
Under most circumstances, it's enough for a user-defined type to derive from finalizable and leave it at that. However, the above 2 methods are provided as virtual for customization's sake. When a finalizable object is reclaimed by the RCU subsystem, it will call the safe_destroy method. The default implementation simply calls the object's destructor and frees the memory associated to it. If, for whatever reason, a user wants to override this behaviour, they may do so by extending either of those methods.
@@ -327,12 +185,12 @@
Miscellaneous functions
These functions don't really belong anywhere else, but they are included in this file for convenience's sake:
In this implementation, the most expensive operation is undoubtedly sync. It works by locking the global registry, then checking if any thread is in a critical section, and sleeping for short periods of time in case there are. The overhead associated to sync is the main reason why critical sections should be short, and also why finalizable objects are accumulated instead of being reclaimed right away.
-
Interlude: optionals
-
-
#include <xrcu/optional.hpp>
-
-
Before moving on to the topic of containers, we need to take a look at an auxiliary structure implemented to make things easier and more convenient for users. This is the optional type.
-
-
An optional is a template type that can hold either an object of type T, or be in an uninitialized state. In other words, the value may or may not be present. Unlike the usage of a pointer which may be null, an optional never performs dynamic memory allocation, as the space required to hold the value is always there, even if it's not being used.
-
-
Optional types have been standardized in C++17, and are present in many other libraries as well. However, since XRCU aims to work with the base minimum of C++11 compliant compilers, and because it's designed not to depend on other libraries or frameworks, it contains its own (lightweight) implementation.
-
-
Optional API
-
-
An optional is defined as such:
-
-
template <class T>
- struct optional
- {
- };
-
-
And its public interface is the following:
-
-
-
-
optional ();
-
-
-
Default constructor. Initializes the optional without a value.
-
-
-
optional (const T& value);
-
-
-
Initializes the optional to contain value.
-
-
-
optional (const optional<T>& other);
-
-
-
Copy constructor. If other has a value, then it initializes the optional to contain that same value. Otherwise, the optional will contain no value.
-
-
-
optional (T&& value);
-
-
-
Move constructor. Initializes the optional by taking ownership of value.
-
-
-
optional (optional<T>&& other);
-
-
-
Move constructor. If other has a value, then the optional is initialized by taken ownership of it. Otherwise, the optional will have no value.
-
-
-
T& operator* ();
-
-
-
-
const T& operator* () const;
-
-
-
Returns a (possibly const) reference to the optional's value. The results are undefined if the optional does not contain one.
-
-
-
T* operator-> ();
-
-
-
-
const T* operator-> () const;
-
-
-
Returns a (possibly const) pointer to the optional's value. The results are undefined if the optional does not contain one.
-
-
-
bool has_value () const;
-
-
-
Returns true if the optional has a value.
-
-
-
void reset () const;
-
-
-
If the optional contains a value, call its destructor and make the optional contain no value afterwards. Otherwise, there are no effects.
-
-
-
optional& operator= (const optional<T>& other);
-
-
-
If other has a value, assigns it to the optional. Otherwise, calls reset on the optional and leaves it without a value. Returns *this.
-
-
-
optional& operator= (const T& value);
-
-
-
Assigns value to the optional. Returns *this.
-
-
-
optional& operator= (optional<T>&& other);
-
-
-
If other has a value, assigns it to the optional by moving it. Otherwise, calls reset on the optional and leaves it without a value. Returns *this.
-
-
-
optional& operator= (T&& value);
-
-
-
Assigns value to the optional by moving it. Returns *this.
-
-
-
~optional ();
-
-
-
Destroys the value associated to the optional, if it had any.
-
-
-
-
-
Implementation details
-
-
Optionals are rather simple: They are implemented by using a flat buffer on which placement new is called. Once an object has been constructed, a pointer is cached for the buffer. This pointer will be used when fetching the value, or destroying the object (it's null for empty optionals).
-
-
Optionals are very useful when performing lookups in mapped containers, since they allow us to bypass the need of additional output parameters to determine if a search was successful in an atomic way.
-
-
Another reason as to why optional values are used so extensively in XRCU is because we consider them to be the best way to signal both a valid object, as well as an error. When using lock-less data structures, it's impossible to determine beforehand whether an error will occur when calling a method (because multithreading makes things non deterministic). Throwing an exception was considered a bit too "punishing" - it's not precisely a programming error, after all.
-
Stacks
-
#include <xrcu/stack.hpp>
+
#include <xrcu/stack.hpp>
Stacks are the simplest of the lock-free data structures: They represent a basic LIFO container on which you can push and pop items at the back of the stack. Although their functionality is a bit more limited than other structures, they are still very useful to implement things like atomic free lists.
@@ -499,20 +232,20 @@
Stack API
Stacks are templated types that can be instantiated with a type T:
Same as push, only it constructs a new item by calling the move constructor with args....
-
optional<T> pop ();
+
std::optional<T> pop ();
Removes the item at the top of the stack, and returns an optional with that value. If the stack is empty, the method returns an optional with no value.
-
optional<T> top ();
+
std::optional<T> top ();
Fetches the current item at the top of the stack, and returns an optional with it as its value. If the stack is empty, returns an optional with no value.
@@ -612,49 +345,49 @@
Stack API
Returns true if the stack is empty (equivalent to size () == 0).
-
void swap (stack<T>& other);
+
void swap (stack<T, Alloc>& other);
Swaps the contents of the stack with other, but only if other is not the same object.
-
stack<T>& operator= (const stack<T>& other);
+
stack& operator= (const stack& other);
Assigns the contents of other to the stack and returns *this.
-
stack<T>& operator= (stack<T>&& other);
+
stack& operator= (stack&& other);
Move assignment. Takes ownership of the elements in other. Returns *this.
-
bool operator== (const stack<T>& other) const;
+
bool operator== (const stack& other) const;
Compares the elements of the stack with the ones in other. Returns true if they are all equal.
-
bool operator!= (const stack<T>& other) const;
+
bool operator!= (const stack& other) const;
Compares the elements of the stack with the ones in other. Returns true if any two of them are not equal.
-
bool operator< (const stack<T>& other) const;
+
bool operator< (const stack& other) const;
-
bool operator> (const stack<T>& other) const;
+
bool operator> (const stack& other) const;
-
bool operator<= (const stack<T>& other) const;
+
bool operator<= (const stack& other) const;
-
bool operator>= (const stack<T>& other) const;
+
bool operator>= (const stack& other) const;
Lexicographically compares the elements of the stack with the ones in other, in a way that is equivalent to calling std::lexicographical_compare.
Assigns to the stack the elements described by (x, y). They could be an iterator range, or a pair of (integer, value), as is the case with the stack's constructor.
@@ -754,17 +487,17 @@
Stack API
-
Implementation details
+
Implementation details
There really isn't much to say about the internal details of the stack. It's probably the easiest lock free structure to implement. As with most other implementations, the one in XRCU simply consists of an atomic pointer to the top node. The use of RCU prevents the biggest issue with this design, that is, the ABA problem.
-
The only noteworthy thing to point out is that the swap method is safe to call from multiple threads as well. In order to achive this, the library uses a special sentinel bit, that is temporarily set as the head node when a swap is undergoing. During a swap, push and pop cannot proceed, since they check against that the special bit is not set before modifying the stack.
+
The only noteworthy thing to point out is that the swap method is safe to call from multiple threads as well. In order to achive this, the library uses a special sentinel bit that is temporarily set at the head node when a swap is undergoing. During a swap, push and pop cannot proceed, since they check that this special bit is not set before modifying the stack.
Note that because stack iterators are implicit critical sections, and because of stacks' implementation, iterating a stack will always be safe, even in the presence of operations like push and pop. The only way for an iterator to be invalidated is it's at the beggining of the stack, and a call to pop is made. Even then, dereferencing the iterator is valid, but advancing it will end prematurely, since the object was unlinked from the stack.
Queues
-
#include <xrcu/queue.hpp>
+
#include <xrcu/queue.hpp>
This is a multi-producer, multi-consumer, FIFO queue. Much like the stack, elements can be pushed or popped from it, but the order is different: They will be retrieved in the same order they were inserted.
@@ -774,20 +507,20 @@
Queue API
Queues are templated types, and they may be instantiated with a type T:
As with the stack, a queue's iterator can be considered a forward iterator, and it's also a cs_guard, so that a queue may be examined by an iterator, at any time.
@@ -801,13 +534,13 @@
Queue API
Default constructor. Initializes a queue to be empty.
-
template <class Integer> queue (Integer n, T value);
+
template <typename Integer> queue (Integer n, T value);
Initializes a queue to contain n times value.
-
template <class Iter> queue (Iter first, Iter last);
+
template <typename Iter> queue (Iter first, Iter last);
Initializes a queue with the values in the range [first, last).
@@ -819,13 +552,13 @@
Queue API
Initializes a queue with the values in lst.
-
queue (const queue<T>& other);
+
queue (const queue& other);
Copy constructor. Initializes a queue with the values in other.
-
queue (queue<T>&& other);
+
queue (queue&& other);
Move constructor. Takes ownership of the values in other.
Assigns to the queue the elements described by (x, y). They can be an iterator range or a pair of (integer, value), as is the case with the queue's constructor.
@@ -1005,7 +738,7 @@
Queue API
-
Implementation details
+
Implementation details
The design of the multi-producer, multi-consumer queue in XRCU is rather simple and different from other implementations. It consists of a single array that holds either the elements themselvers, or pointers to them, a decision taken at compile time via type traits.
@@ -1017,7 +750,7 @@
Implementation details
Skip lists
-
#include <xrcu/skip_list.hpp>
+
#include <xrcu/skip_list.hpp>
A skip list is an associative container that holds a sorted set of unique objects of a particular type. In addition to the Key type, skip lists are instantiated with a comparator type that allows them to determine ordering.
@@ -1027,23 +760,24 @@
Skip list API
Skip lists are templated types, defined in the following way:
The template parameter T refers to the key type, whereas Cmp is the comparator type, which defaults to std::less. Under most circumstances, that is usually enough, but users may instantiate with any other type that defines the operator() which returns a boolean.
@@ -1059,7 +793,7 @@
Skip list API
Initializes the skip list with comparator c, which defaults to a default constructed value. Also takes a parameter indicating the maximum depth a skip list node may have, which defaults to an implementation-specified value. Users shouldn't need to change the latter, but it can be useful when tuning the application for performance. As a general rule, a higher value implies more memory usage, but better performance.
-
template <class Iter> skip_list (Iter first, Iter last, Cmp c = Cmp (), unsigned int depth = ...);
+
template <typename Iter> skip_list (Iter first, Iter last, Cmp c = Cmp (), unsigned int depth = ...);
Initializes the skip list to the values in [first, last). The comparator and depth parameters are the same as explained above.
@@ -1071,19 +805,19 @@
Skip list API
Initializes the skip list to the values in lst. The comparator and depth parameters are the same as explained above.
-
skip_list (const skip_list<T, Cmp>& other);
+
skip_list (const skip_list& other);
Copy constructor. Initializes the skip list to hold the values in other.
-
skip_list (skip_list<T, Cmp>&& other);
+
skip_list (skip_list&& other);
Move constructor. Takes ownership of the values in other.
-
optional<T> find (const T& key) const;
+
std::optional<T> find (const T& key) const;
Searches for an element equivalent to key in the skip list, and returns an optional with it as its value. If the key couldn't be found, the returned optional has no value.
@@ -1107,7 +841,7 @@
Skip list API
Erases key from the skip list. Returns true if the key was present previous to this call.
-
optional<T> remove (const T& key);
+
std::optional<T> remove (const T& key);
Erases key from the skip list and returns an optional with that element as its value, if the key was present. Otherwise, the returned optional has no value.
@@ -1151,7 +885,7 @@
Skip list API
Returns true if the skip list is empty (i.e: its size is 0).
-
template <class Iter> void assign (Iter first, Iter last);
+
template <typename Iter> void assign (Iter first, Iter last);
Assigns to the skip list the elements in [first, last).
Assigns the elements in other to the skip list. Returns *this.
-
skip_list& operator= (skip_list<T, Cmp>&& other);
+
skip_list& operator= (skip_list&& other);
Move assignment. Takes ownership of the elements in other. Returns *this.
-
void swap (skip_list<T, Cmp>& other);
+
void swap (skip_list& other);
Swaps the contents of the skip list with other, but only if other is a different object.
@@ -1189,7 +923,7 @@
Skip list API
-
Implementation details
+
Implementation details
In XRCU, skip lists are implemented as described in any piece of literature that talks about them. Basically, every skip list of depth D has a head node that can be linked with up to other D nodes. When performing a lookup for an element, we start at the head node, and move horizontally until the current element is equal or greater. If it's equal, we the lookup succeeded. Otherwise, we move vertically to the next node, until we either find the element, or we exhausted every node.
@@ -1201,7 +935,7 @@
Implementation details
Hash tables
-
#include <xrcu/hash_table.hpp>
+
#include <xrcu/hash_table.hpp>
Hash tables are associative containers that map unique keys to values. Ordering is unspecified for both keys and values. In addition to the key and value types, hash tables are instantiated with a hashing type and an equality type; callables that compute a hash value for a given key, and one that tests for equality, given two keys, respectively.
@@ -1213,26 +947,27 @@
Hash table API
As mentioned above, hash tables are template types, defined like this:
The template parameters should be pretty self explanatory: They refer to the key, value, equality and hashing types, in that order. The Equal type has to operate on two keys and return a boolean value that determines their equality, whereas the Hash type has to operate on keys and return unsigned integers. Both are allowed to throw exceptions, although it is not really wise to do so.
@@ -1248,25 +983,25 @@
Hash table API
Initializes the hash table to hold size elements, with a load factor of lf, using the equality predicate e and the hasher h. The load factor must be in the range [0.4, 0.9]; if it's not, then it will silently be set to the default of 0.85.
-
template <class It> hash_table (It first, It last, float lf = 0.85, Equal e = Equal (), Hash h = Hash ())
+
template <typename It> hash_table (It first, It last, float lf = 0.85, Equal e = Equal (), Hash h = Hash ())
Initializes the hash table with the values between (first, last]. The elements in that range must have two public members defined: first and second, referring to the key and value of each element, respectively, and in a similar fashion to what std::pair does. The other parameters work as with the previous constructor.
-
hash_table (std::initializer_list<std::pair<Key, Val> >, float lf = 0.85, Equal e = Equal (), Hash h = Hash ())
+
hash_table (std::initializer_list<std::pair<Key, Val>>, float lf = 0.85, Equal e = Equal (), Hash h = Hash ())
Initializes the hash table with the values in lst. The rest of the parameters work as with the previous constructors.
Updates the value associated to key by calling f with it and the rest of the arguments. In other words, if val is the value associated to key, calls f (val, args...) and updates the value with the result. If no value was present for key, the function is called with a default constructed value instead (as if by calling Val ()).
-
Note that the function takes a reference to the value, and so it's possible to modify it in place. Additionally, if the function returns the very same object that was passed (i.e: the same reference that it received), there won't be any updates made. The function can also return a freshly made object, and update will work the same. The function may be called more than once if the atomic updates fail.
+
Note that the function takes a reference to the value, and so it's possible to modify it in place. Additionally, if the function returns the very same object that was passed (i.e: the same reference that it received), there won't be any modifications made to the value in the table. If, however, the function returns a different object, this call will need to atomically update the value. The passed function may be called more than once if the atomic updates fail.
Returns true if key was not present in the hash table before the call.
@@ -1340,7 +1075,7 @@
Hash table API
Removes every element from the hash table.
-
template <class Iter> void assign (Iter first, Iter last);
+
template <typename Iter> void assign (Iter first, Iter last);
Assigns the elements in [first, last) to the hash table. The same restrictions as the range constructor apply here.
Move assignment. Takes ownership of the elements in other. Returns *this.
-
void swap (hash_table<Key, Val>& other);
+
void swap (hash_table& other);
Swaps the contents of the hash table with other, but only if other is a different object.
@@ -1444,7 +1179,7 @@
Hash table API
-
Implementation details
+
Implementation details
Hash tables are somewhat complex, because the atomicity requirements force us to do some rather convoluted things. To start off, a hash table is essentially a vector of consecutive key and value pairs, with some special values indicating free and deleted entries. However, since we can only operate atomically on integers, we wrap any other type that is not integral into a dynamically allocated pointer. This is done based on the template instantiation and is figured out at compile time.
diff --git a/docs/xrcu.pod b/docs/xrcu.pod
index e7c7bef..7cfe474 100644
--- a/docs/xrcu.pod
+++ b/docs/xrcu.pod
@@ -262,126 +262,6 @@ The overhead associated to C is the main reason why critical sections
should be short, and also why C objects are accumulated instead of
being reclaimed right away.
-=head2 Interlude: optionals
-
- #include
-
-Before moving on to the topic of containers, we need to take a look at an
-auxiliary structure implemented to make things easier and more convenient
-for users. This is the C type.
-
-An optional is a template type that can hold either an object of type I, or
-be in an uninitialized state. In other words, the value may or may not be
-present. Unlike the usage of a pointer which may be null, an C never
-performs dynamic memory allocation, as the space required to hold the value is
-always there, even if it's not being used.
-
-Optional types have been standardized in C++17, and are present in many other
-libraries as well. However, since XRCU aims to work with the base minimum of
-C++11 compliant compilers, and because it's designed not to depend on other
-libraries or frameworks, it contains its own (lightweight) implementation.
-
-=head3 Optional API
-
-An optional is defined as such:
-
- template
- struct optional
- {
- };
-
-And its public interface is the following:
-
-=over 4
-
-=item optional ();
-
-Default constructor. Initializes the optional without a value.
-
-=item optional (const T& value);
-
-Initializes the optional to contain C.
-
-=item optional (const optional& other);
-
-Copy constructor. If C has a value, then it initializes the optional to
-contain that same value. Otherwise, the optional will contain no value.
-
-=item optional (T&& value);
-
-Move constructor. Initializes the optional by taking ownership of C.
-
-=item optional (optional&& other);
-
-Move constructor. If C has a value, then the optional is initialized
-by taking ownership of it. Otherwise, the optional will have no value.
-
-=item T& operator* ();
-
-=item const T& operator* () const;
-
-Returns a (possibly const) reference to the optional's value. The results are
-undefined if the optional does not contain one.
-
-=item T* operator-> ();
-
-=item const T* operator-> () const;
-
-Returns a (possibly const) pointer to the optional's value. The results are
-undefined if the optional does not contain one.
-
-=item bool has_value () const;
-
-Returns true if the optional has a value.
-
-=item void reset () const;
-
-If the optional contains a value, call its destructor and make the optional
-contain no value afterwards. Otherwise, there are no effects.
-
-=item optional& operator= (const optional& other);
-
-If C has a value, assigns it to the optional. Otherwise, calls C
-on the optional and leaves it without a value. Returns C<*this>.
-
-=item optional& operator= (const T& value);
-
-Assigns C to the optional. Returns C<*this>.
-
-=item optional& operator= (optional&& other);
-
-If C has a value, assigns it to the optional by moving it. Otherwise,
-calls C on the optional and leaves it without a value. Returns C<*this>.
-
-=item optional& operator= (T&& value);
-
-Assigns C to the optional by moving it. Returns C<*this>.
-
-=item ~optional ();
-
-Destroys the value associated to the optional, if it had any.
-
-=back
-
-=head3 Implementation details
-
-Optionals are rather simple: They are implemented by using a flat buffer
-on which placement new is called. Once an object has been constructed,
-a pointer is cached for the buffer. This pointer will be used when fetching
-the value, or destroying the object (it's null for empty optionals).
-
-Optionals are very useful when performing lookups in mapped containers, since
-they allow us to bypass the need of additional output parameters to determine
-if a search was successful in an atomic way.
-
-Another reason as to why optional values are used so extensively in XRCU is
-because we consider them to be the best way to signal both a valid object,
-as well as an error. When using lock-less data structures, it's impossible to
-determine beforehand whether an error will occur when calling a method (because
-multithreading makes things non deterministic). Throwing an exception was
-considered a bit too "punishing" - it's not precisely a programming error,
-after all.
-
=head2 Stacks
#include
@@ -397,7 +277,7 @@ In XRCU, stacks meet the requirements for the C++ concept of I.
Stacks are templated types that can be instantiated with a type T:
- template
+ template >
struct stack
{
typedef T value_type;
@@ -426,11 +306,11 @@ The following describes the public interface for C>
Default constructor. Initializes a stack to be empty.
-=item template stack (Integer n, T value);
+=item template stack (Integer n, T value);
Initializes a stack to contain C times C.
-=item template stack (Iter first, Iter last);
+=item template stack (Iter first, Iter last);
Initializes a stack with the values in the range [C, C)
@@ -450,25 +330,25 @@ Move constructor. Takes ownership of the values in C.
Pushes C to the top of the stack.
-=item template void push (Iter first, Iter last)
+=item template void push (Iter first, Iter last)
Pushes the values in the range [C, C) to the stack.
-=item template void push (Integer n, T value)
+=item template void push (Integer n, T value)
Pushes C times C to the stack.
-=item template void emplace (Args&& ...args)
+=item template void emplace (Args&& ...args)
Same as C, only it constructs a new item by calling the move
constructor with C.
-=item optional pop ();
+=item std::optional pop ();
Removes the item at the top of the stack, and returns an optional with that
value. If the stack is empty, the method returns an optional with no value.
-=item optional top ();
+=item std::optional top ();
Fetches the current item at the top of the stack, and returns an optional
with it as its value. If the stack is empty, returns an optional with no value.
@@ -485,36 +365,36 @@ Returns the maximum allowed size for the stack.
Returns true if the stack is empty (equivalent to C).
-=item void swap (stack& other);
+=item void swap (stack& other);
Swaps the contents of the stack with C, but only if C is not the
same object.
-=item stack& operator= (const stack& other);
+=item stack& operator= (const stack& other);
Assigns the contents of C to the stack and returns C<*this>.
-=item stack& operator= (stack&& other);
+=item stack& operator= (stack&& other);
Move assignment. Takes ownership of the elements in C. Returns C<*this>.
-=item bool operator== (const stack& other) const;
+=item bool operator== (const stack& other) const;
Compares the elements of the stack with the ones in C. Returns true
if they are all equal.
-=item bool operator!= (const stack& other) const;
+=item bool operator!= (const stack& other) const;
Compares the elements of the stack with the ones in C. Returns true
if any two of them are not equal.
-=item bool operator< (const stack& other) const;
+=item bool operator< (const stack& other) const;
-=item bool operator> (const stack& other) const;
+=item bool operator> (const stack& other) const;
-=item bool operator<= (const stack& other) const;
+=item bool operator<= (const stack& other) const;
-=item bool operator>= (const stack& other) const;
+=item bool operator>= (const stack& other) const;
Lexicographically compares the elements of the stack with the ones in C,
in a way that is equivalent to calling C.
@@ -523,7 +403,7 @@ in a way that is equivalent to calling C.
Removes every element from the stack.
-=item template void assign (T1 x, T2 y);
+=item template void assign (T1 x, T2 y);
Assigns to the stack the elements described by (C, C). They could be an
iterator range, or a pair of (integer, value), as is the case with the stack's
@@ -620,7 +500,7 @@ In XRCU, queues meet the requirements for the C++ concept of I.
Queues are templated types, and they may be instantiated with a type T:
- template
+ template >
struct queue
{
typedef T value_type;
@@ -647,11 +527,11 @@ The following describes the public interface for C>
Default constructor. Initializes a queue to be empty.
-=item template queue (Integer n, T value);
+=item template queue (Integer n, T value);
Initializes a queue to contain C times C.
-=item template queue (Iter first, Iter last);
+=item template queue (Iter first, Iter last);
Initializes a queue with the values in the range [C, C).
@@ -659,11 +539,11 @@ Initializes a queue with the values in the range [C, C).
Initializes a queue with the values in C.
-=item queue (const queue& other);
+=item queue (const queue& other);
Copy constructor. Initializes a queue with the values in C.
-=item queue (queue&& other);
+=item queue (queue&& other);
Move constructor. Takes ownership of the values in C.
@@ -671,22 +551,22 @@ Move constructor. Takes ownership of the values in C.
Pushes C to the top of the queue.
-=item template void emplace (Args&& ...args)
+=item template void emplace (Args&& ...args)
Same as C, only the new item is constructed by calling the move
constructor with C.
-=item optional pop ();
+=item std::optional pop ();
Removes the first item from the queue, and returns an optional with that value.
If the queue is empty, the method returns an optional with no value.
-=item optional front () const;
+=item std::optional front () const;
Returns an optional with the first value from the queue. If the queue is empty,
an optional with no value is returned instead.
-=item optional back () const;
+=item std::optional back () const;
Returns an optional with the last value from the queue. If the queue is empty,
an optional with no value is returned instead.
@@ -699,36 +579,36 @@ Returns the size of the queue.
Returns the maximum allowed size for the queue.
-=item void swap (queue& other);
+=item void swap (queue& other);
Swaps the contents of the queue with C, but only if C is not
the same object.
-=item queue& operator= (const queue& other);
+=item queue& operator= (const queue& other);
Assigns the contents of C to the queue and returns C<*this>.
-=item queue& operator= (queue&& other);
+=item queue& operator= (queue&& other);
Move assignment. Takes ownership of the elements in C. Returns C<*this>.
-=item bool operator== (const queue& other);
+=item bool operator== (const queue& other);
Compares the elements of the queue with the ones in C. Returns true
if they are all equal.
-=item bool operator!= (const queue& other);
+=item bool operator!= (const queue& other);
Compares the elements of the queue with the ones in C. Returns true
if any two of them are not equal.
-=item bool operator< (const queue& other) const;
+=item bool operator< (const queue& other) const;
-=item bool operator> (const queue& other) const;
+=item bool operator> (const queue& other) const;
-=item bool operator<= (const queue& other) const;
+=item bool operator<= (const queue& other) const;
-=item bool operator>= (const queue& other) const;
+=item bool operator>= (const queue& other) const;
Lexicographically compares the elements of the queue with the ones in C,
in a way that is equivalent to calling C.
@@ -737,7 +617,7 @@ in a way that is equivalent to calling C.
Removes every element from the queue.
-=item template void assign (T1 x, T2 y);
+=item template void assign (T1 x, T2 y);
Assigns to the queue the elements described by (C, C). They can be an
iterator range or a pair of (integer, value), as is the case with the queue's
@@ -834,7 +714,8 @@ and I.
Skip lists are templated types, defined in the following way:
- template >
+ template ,
+ typename Alloc = std::allocator>
struct skip_list
{
typedef T value_type;
@@ -874,7 +755,7 @@ shouldn't need to change the latter, but it can be useful when tuning the
application for performance. As a general rule, a higher value implies more
memory usage, but better performance.
-=item template skip_list (Iter first, Iter last, Cmp c = Cmp (), unsigned int depth = ...);
+=item template skip_list (Iter first, Iter last, Cmp c = Cmp (), unsigned int depth = ...);
Initializes the skip list to the values in [C, C). The comparator
and depth parameters are the same as explained above.
@@ -884,15 +765,15 @@ and depth parameters are the same as explained above.
Initializes the skip list to the values in C. The comparator and depth
parameters are the same as explained above.
-=item skip_list (const skip_list& other);
+=item skip_list (const skip_list& other);
Copy constructor. Initializes the skip list to hold the values in C.
-=item skip_list (skip_list&& other);
+=item skip_list (skip_list&& other);
Move constructor. Takes ownership of the values in C.
-=item optional find (const T& key) const;
+=item std::optional find (const T& key) const;
Searches for an element equivalent to C in the skip list, and returns an
optional with it as its value. If the key couldn't be found, the returned
@@ -912,7 +793,7 @@ previous to this call.
Erases C from the skip list. Returns true if the key was present previous
to this call.
-=item optional remove (const T& key);
+=item std::optional remove (const T& key);
Erases C from the skip list and returns an optional with that element as
its value, if the key was present. Otherwise, the returned optional has no
@@ -942,7 +823,7 @@ Returns the maximum allowed size for a skip list.
Returns true if the skip list is empty (i.e: its size is 0).
-=item template void assign (Iter first, Iter last);
+=item template void assign (Iter first, Iter last);
Assigns to the skip list the elements in [C, C).
@@ -950,15 +831,15 @@ Assigns to the skip list the elements in [C, C).
Assigns to the skip list the elements in C.
-=item skip_list& operator= (const skip_list& other);
+=item skip_list& operator= (const skip_list& other);
Assigns the elements in C to the skip list. Returns C<*this>.
-=item skip_list& operator= (skip_list&& other);
+=item skip_list& operator= (skip_list&& other);
Move assignment. Takes ownership of the elements in C. Returns C<*this>.
-=item void swap (skip_list& other);
+=item void swap (skip_list& other);
Swaps the contents of the skip list with C, but only if C is a
different object.
@@ -1016,9 +897,10 @@ and I.
As mentioned above, hash tables are template types, defined like this:
- template ,
- class Hash = std::hash >
+ template ,
+ typename Hash = std::hash,
+ typename Alloc = std::allocator>>
struct hash_table
{
typedef Val mapped_type;
@@ -1057,7 +939,7 @@ C, using the equality predicate C and the hasher C. The load factor
must be in the range [0.4, 0.9]; if it's not, then it will silently be set to
the default of 0.85.
-=item template hash_table (It first, It last, float lf = 0.85, Equal e = Equal (), Hash h = Hash ())
+=item template hash_table (It first, It last, float lf = 0.85, Equal e = Equal (), Hash h = Hash ())
Initializes the hash table with the values between (C, C]. The
elements in that range must have two public members defined: C and
@@ -1065,16 +947,16 @@ C, referring to the key and value of each element, respectively, and
in a similar fashion to what C does. The other parameters work as
with the previous constructor.
-=item hash_table (std::initializer_list >, float lf = 0.85, Equal e = Equal (), Hash h = Hash ())
+=item hash_table (std::initializer_list>, float lf = 0.85, Equal e = Equal (), Hash h = Hash ())
Initializes the hash table with the values in C. The rest of the parameters
work as with the previous constructors.
-=item hash_table (const hash_table& other);
+=item hash_table (const hash_table& other);
Copy constructor. Initializes the hash table with the values in C.
-=item hash_table (hash_table&& other);
+=item hash_table (hash_table&& other);
Move constructor. Takes ownership of the values in C.
@@ -1090,7 +972,7 @@ Returns the maximum allowed size for a hash table.
Returns true if the hash table is empty.
-=item optional find (const Key& key) const;
+=item std::optional find (const Key& key) const;
=item Val find (const Key& key, const Val& defl) const;
@@ -1107,7 +989,7 @@ Returns true if the key is present in the hash table.
Associates C with C in the hash table. Returns true if the value was
not present. Otherwise, the former value is replaced.
-=item template bool update (const Key& key, Fn f, Args... args);
+=item template bool update (const Key& key, Fn f, Args... args);
Updates the value associated to C by calling C with it and the rest of
the arguments. In other words, if C is the value associated to C,
@@ -1138,7 +1020,7 @@ to if, if it was present.
Removes every element from the hash table.
-=item template void assign (Iter first, Iter last);
+=item template void assign (Iter first, Iter last);
Assigns the elements in [C, C) to the hash table. The same
restrictions as the range constructor apply here.
@@ -1147,15 +1029,15 @@ restrictions as the range constructor apply here.
Assigns the elements in C to the hash table.
-=item hash_table& operator= (const hash_table& other);
+=item hash_table& operator= (const hash_table& other);
Assigns the elements in C to the hash table. Returns C<*this>.
-=item hash_table& operator= (hash_table&& other);
+=item hash_table& operator= (hash_table&& other);
Move assignment. Takes ownership of the elements in C. Returns C<*this>.
-=item void swap (hash_table& other);
+=item void swap (hash_table& other);
Swaps the contents of the hash table with C, but only if C is a
different object.
diff --git a/optional.hpp b/optional.hpp
deleted file mode 100644
index d56eef2..0000000
--- a/optional.hpp
+++ /dev/null
@@ -1,175 +0,0 @@
-/* Declarations for the optional template type.
-
- This file is part of xrcu.
-
- xrcu is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see . */
-
-#ifndef __XRCU_OPTIONAL_HPP__
-#define __XRCU_OPTIONAL_HPP__ 1
-
-#include
-
-namespace xrcu
-{
-
-template
-struct optional
-{
- T *xptr = nullptr;
- alignas (alignof (T)) char buf[sizeof (T)];
-
- T* _Ptr ()
- {
- return ((T *)&this->buf[0]);
- }
-
- const T* _Ptr () const
- {
- return ((const T *)&this->buf[0]);
- }
-
- void _Init (const T& value)
- {
- new (this->_Ptr ()) T (value);
- this->xptr = this->_Ptr ();
- }
-
- void _Init (T&& value)
- {
- new (this->_Ptr ()) T (std::forward (value));
- this->xptr = this->_Ptr ();
- }
-
- optional ()
- {
- }
-
- optional (const T& value)
- {
- this->_Init (value);
- }
-
- optional (const optional& right)
- {
- if (right.xptr)
- this->_Init (*right);
- }
-
- optional (T&& value)
- {
- this->_Init (std::forward (value));
- }
-
- optional (optional&& right)
- {
- if (right.xptr)
- {
- this->_Init (std::forward (*right));
- right.xptr = nullptr;
- }
- }
-
- T& operator* ()
- {
- return (*this->xptr);
- }
-
- const T& operator* () const
- {
- return (*this->xptr);
- }
-
- T* operator-> ()
- {
- return (this->xptr);
- }
-
- const T* operator-> () const
- {
- return (this->xptr);
- }
-
- bool has_value () const
- {
- return (this->xptr != nullptr);
- }
-
- void reset ()
- {
- if (!this->xptr)
- return;
-
- this->xptr->~T ();
- this->xptr = nullptr;
- }
-
- optional& operator= (const optional& right)
- {
- if (&right == this)
- ;
- else if (!right.xptr)
- this->reset ();
- else if (!this->xptr)
- this->_Init (*right);
- else
- **this = *right;
-
- return (*this);
- }
-
- optional& operator= (optional&& right)
- {
- if (&right == this)
- return (*this);
-
- if (!right.xptr)
- this->reset ();
- else if (!this->xptr)
- this->_Init (std::forward (*right));
- else
- **this = std::forward (*right);
-
- right.xptr = nullptr;
- return (*this);
- }
-
- optional& operator= (const T& value)
- {
- if (!this->xptr)
- this->_Init (value);
- else
- **this = value;
-
- return (*this);
- }
-
- optional& operator= (T&& value)
- {
- if (!this->xptr)
- this->_Init (std::forward (value));
- else
- **this = std::forward (value);
-
- return (*this);
- }
-
- ~optional ()
- {
- this->reset ();
- }
-};
-
-} // namespace xrcu
-
-#endif
diff --git a/skip_list.cpp b/skip_list.cpp
deleted file mode 100644
index a470b83..0000000
--- a/skip_list.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Definitions for the skip list template type.
-
- This file is part of xrcu.
-
- xrcu is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see . */
-
-#include "skip_list.hpp"
-#include
-#include
-
-namespace xrcu
-{
-
-namespace detail
-{
-
-sl_node_base* sl_alloc_node (unsigned int lvl, size_t size, uintptr_t **outpp)
-{
- void *p = ::operator new (size + lvl * sizeof (uintptr_t));
- *outpp = (uintptr_t *)((char *)p + size);
- memset (*outpp, 0, lvl * sizeof (uintptr_t));
- return ((sl_node_base *)p);
-}
-
-void sl_dealloc_node (void *ptr)
-{
- ::operator delete (ptr);
-}
-
-} // namespace detail
-
-} // namespace xrcu
diff --git a/hash_table.cpp b/src/hash_table.cpp
similarity index 68%
rename from hash_table.cpp
rename to src/hash_table.cpp
index b191325..33fc55c 100644
--- a/hash_table.cpp
+++ b/src/hash_table.cpp
@@ -15,7 +15,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
-#include "hash_table.hpp"
+#include "xrcu/hash_table.hpp"
#include
#include
#include
@@ -26,17 +26,6 @@ namespace xrcu
namespace detail
{
-inline ht_vector* ht_vector_make (size_t n)
-{
- void *p = ::operator new (sizeof (ht_vector) + n * sizeof (uintptr_t));
- return (new (p) ht_vector ((uintptr_t *)((char *)p + sizeof (ht_vector))));
-}
-
-void ht_vector::safe_destroy ()
-{
- ::operator delete (this);
-}
-
static const size_t PRIMES[] =
{
0xb, 0x25, 0x71, 0x15b, 0x419, 0xc4d, 0x24f5, 0x6ee3, 0x14cb3, 0x3e61d,
@@ -52,6 +41,11 @@ static const size_t PRIMES[] =
#endif
};
+size_t vec_psize (size_t pidx)
+{
+ return (PRIMES[pidx]);
+}
+
size_t find_hsize (size_t size, float mvr, size_t& pidx)
{
intptr_t i1 = 0, i2 = sizeof (PRIMES) / sizeof (PRIMES[0]);
@@ -68,28 +62,6 @@ size_t find_hsize (size_t size, float mvr, size_t& pidx)
return ((size_t)(PRIMES[i1] * mvr));
}
-ht_vector* make_htvec (size_t pidx, uintptr_t key, uintptr_t val)
-{
- size_t entries = PRIMES[pidx], tsize = table_idx (entries);
-#ifdef XRCU_HAVE_XATOMIC_DCAS
- auto ret = ht_vector_make (tsize + 1);
-
- // Ensure correct alignment for double-width CAS.
- if ((uintptr_t)ret->data % (2 * sizeof (uintptr_t)) != 0)
- ++ret->data;
-#else
- auto ret = ht_vector_make (tsize);
-#endif
-
- for (size_t i = 0; i < tsize; i += 2)
- ret->data[i] = key, ret->data[i + 1] = val;
-
- ret->entries = entries;
- ret->pidx = pidx;
-
- return (ret);
-}
-
} // namespace detail
} // namespace xrcu
diff --git a/lwlock.cpp b/src/lwlock.cpp
similarity index 97%
rename from lwlock.cpp
rename to src/lwlock.cpp
index 03fe5f2..c47492d 100644
--- a/lwlock.cpp
+++ b/src/lwlock.cpp
@@ -15,8 +15,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
-#include "lwlock.hpp"
-#include "xatomic.hpp"
+#include "xrcu/lwlock.hpp"
+#include "xrcu/xatomic.hpp"
#if (defined (linux) || defined (__linux) || defined (__linux__)) && \
defined (__BYTE_ORDER__)
diff --git a/src/queue.cpp b/src/queue.cpp
new file mode 100644
index 0000000..898cdc3
--- /dev/null
+++ b/src/queue.cpp
@@ -0,0 +1,91 @@
+/* Definitions for the queue template type.
+
+ This file is part of xrcu.
+
+ xrcu is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see . */
+
+#include "xrcu/queue.hpp"
+
+namespace xrcu
+{
+
+namespace detail
+{
+
+bool q_base::push (uintptr_t val, uintptr_t xbit, uintptr_t empty)
+{
+ while (true)
+ {
+ size_t curr = this->_Wridx ();
+ if (curr >= this->cap)
+ return (false);
+
+ uintptr_t xv = this->ptrs[curr];
+ if ((xv & xbit) != 0)
+ return (false);
+ else if (xv == empty &&
+ xatomic_cas_bool (&this->ptrs[curr], xv, val))
+ {
+ this->wr_idx.fetch_add (1, std::memory_order_acq_rel);
+ return (true);
+ }
+
+ xatomic_spin_nop ();
+ }
+}
+
+uintptr_t q_base::pop (uintptr_t xbit, uintptr_t dfl)
+{
+ while (true)
+ {
+ size_t curr = this->_Rdidx ();
+ if (curr >= this->_Wridx ())
+ return (dfl);
+
+ uintptr_t rv = this->ptrs[curr];
+ if (rv & xbit)
+ return (xbit);
+ else if (rv != dfl && xatomic_cas_bool (&this->ptrs[curr], rv, dfl))
+ {
+ this->rd_idx.fetch_add (1, std::memory_order_relaxed);
+ return (rv);
+ }
+
+ xatomic_spin_nop ();
+ }
+}
+
+void q_base::replace (std::atomic& ptr, q_base *old,
+ q_base *nq, uintptr_t)
+{
+ finalize (old);
+ ptr.store (nq, std::memory_order_relaxed);
+}
+
+void q_base::clear (std::atomic&, q_base *old,
+ q_base *, uintptr_t empty)
+{
+ old->wr_idx.store (old->cap, std::memory_order_relaxed);
+ old->rd_idx.store (old->cap, std::memory_order_relaxed);
+
+ for (size_t i = 0; i < old->cap; ++i)
+ old->ptrs[i] = empty;
+
+ old->rd_idx.store (0, std::memory_order_release);
+ old->wr_idx.store (0, std::memory_order_release);
+}
+
+} // namespace detail
+
+} // namespace xrcu
diff --git a/stack.cpp b/src/stack.cpp
similarity index 98%
rename from stack.cpp
rename to src/stack.cpp
index 1ad3a91..f918be2 100644
--- a/stack.cpp
+++ b/src/stack.cpp
@@ -15,8 +15,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
-#include "stack.hpp"
-#include "xatomic.hpp"
+#include "xrcu/stack.hpp"
+#include "xrcu/xatomic.hpp"
#include
namespace xrcu
diff --git a/utils.cpp b/src/utils.cpp
similarity index 97%
rename from utils.cpp
rename to src/utils.cpp
index f30eb5a..f7e0f8a 100644
--- a/utils.cpp
+++ b/src/utils.cpp
@@ -15,7 +15,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
-#include "utils.hpp"
+#include "xrcu/utils.hpp"
#include
namespace xrcu
diff --git a/xrcu.cpp b/src/xrcu.cpp
similarity index 99%
rename from xrcu.cpp
rename to src/xrcu.cpp
index c89b632..a13a67b 100644
--- a/xrcu.cpp
+++ b/src/xrcu.cpp
@@ -15,9 +15,9 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
-#include "xrcu.hpp"
-#include "xatomic.hpp"
-#include "version.hpp"
+#include "xrcu/xrcu.hpp"
+#include "xrcu/xatomic.hpp"
+#include "xrcu/version.hpp"
#include
#include
#include
diff --git a/tests/hash.hpp b/tests/hash.hpp
index ae39631..b515ebd 100644
--- a/tests/hash.hpp
+++ b/tests/hash.hpp
@@ -1,13 +1,14 @@
#ifndef __XRCU_TESTS_HASH__
#define __XRCU_TESTS_HASH__ 1
-#include "../hash_table.hpp"
+#include "xrcu/hash_table.hpp"
#include "utils.hpp"
#include
#include
#include
-typedef xrcu::hash_table table_t;
+typedef xrcu::hash_table,
+ std::hash, test_allocator> table_t;
namespace ht_test
{
diff --git a/tests/opt.hpp b/tests/opt.hpp
deleted file mode 100644
index 0fd598a..0000000
--- a/tests/opt.hpp
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef __XRCU_TESTS_OPT__
-#define __XRCU_TESTS_OPT__ 1
-
-#include "../optional.hpp"
-#include "utils.hpp"
-
-namespace opt_test
-{
-
-void test_optional ()
-{
- xrcu::optional opt;
-
- ASSERT (!opt.has_value ());
- opt.reset ();
- ASSERT (!opt.has_value ());
- opt = mkstr (100);
- ASSERT (opt.has_value ());
-
- *opt += mkstr (200);
- ASSERT (*opt == mkstr (100) + mkstr (200));
-
- opt->clear ();
- ASSERT (opt.has_value ());
- ASSERT (opt->empty ());
-
- opt.reset ();
- ASSERT (!opt.has_value ());
-
- std::string s = mkstr (300);
- opt = s;
- ASSERT (s == *opt);
-
- xrcu::optional o2 { opt };
- ASSERT (*o2 == *opt);
-
- o2.reset ();
- ASSERT (opt.has_value ());
-
- xrcu::optional o3 { mkstr (400) };
- ASSERT (o3.has_value ());
-}
-
-test_module optional_tests
-{
- "optional",
- {
- { "API", test_optional }
- }
-};
-
-} // namespace opt_test
-
-#endif
diff --git a/tests/queue.hpp b/tests/queue.hpp
index f81603a..3d74d91 100644
--- a/tests/queue.hpp
+++ b/tests/queue.hpp
@@ -1,20 +1,21 @@
#ifndef __XRCU_TESTS_QUEUE__
#define __XRCU_TESTS_QUEUE__ 1
-#include "../queue.hpp"
+#include "xrcu/queue.hpp"
#include "utils.hpp"
#include
namespace q_test
{
-typedef xrcu::queue queue_t;
+typedef xrcu::queue