Reader locks

typedef x::ref<valueObj> valueRef;

typedef x::ptr<valueObj> valuePtr;

typedef x::hier<keyClass,valueRef> hierclass;

keyClass keyelement;

std::list<keyClass> key;

hierclass h=hierclass::create();

hierclass::base::readlock lock=h->create_readlock();

A reader lock implements navigation of a hierachical container, without making any changes to it. Note, though, that after retrieving a value object from the container, there's nothing that prohibits the contents of the referenced object from being modified, but this is outside the scope covered by the reader lock. Multiple reader locks can get created concurrently.

A reader lock is a reference-counted object that can be freely passed around. Note that although the reader and writer locks implement thread-safe access to the hierarchical container, each individual reader or a writer lock can only be accessed by one thread a time.

create_readlock() creates a new reader lock. If there's an existing writer lock, create_readlock() waits until the writer lock goes out of scope and gets destroyed.

auto lock2=lock->clone();

Each reader lock represents a key in the hierarchy, either a key with a value, or an intermediate key without a value. A new reader lock created by create_readlock() initially represents the hierarchy's root, or an empty key. The root node is no different than any other node in the hierarchy, and may even have a value, but it always exists even in an otherwise completely empty hierarchy. clone() results in another reader lock, that initially points to the same key.

Note

Always use clone() to create another read lock. Invoking create_readlock() again can result in a deadlock with a pending writer lock from another thread. A pending writer lock may block new reader locks, and the existing reader lock in the same thread can trigger a deadlock, with the thread waiting for the writer lock, and the writer lock thread waiting for the existing reader lock to go away.

valuePtr ptr=lock->entry();

bool flag=lock->exists();

const std::list<keyClass> &name=lock->name();

entry() returns a x::ptr of the value of the lock's current node, which may be null if this is an intermediate node, without a value. exists() checks if the x::ptr would be null. Finally, name() returns the current node's key.

keyelement element;
key valuekey;

bool flag=lock->parent();

bool flag=lock->child(element);

std::set<keyClass> children=lock->children();

bool flag=lock->child(key, true);

parent() returns true if the lock's current node has a parent node. parent() returns false if the lock's current node is the hierarchy's root node, with a null key, true otherwise, since all other nodes must have a parent node.

child() takes a key element, and returns a flag indicating whether the current node has an inferior, or a child node for the given key; either a node with a value, or an intermediate node without a value. children() enumerates all such nodes that exist.

The first argument of the two argument version of child() is a list of key elements, a complete key path. If the second argument is false, child() returns true only if the specified inferior node, to the lock's current node, exists and has a value. If the second argument is true child() returns true if the specified inferior node exists, with a value, or if there's any intermediate node with a value.

bool flag=lock->to_parent();

bool flag=lock->to_child(element);

bool flag=lock->to_child(key, true);

to_parent() and to_child() are similar to parent() and child(), but they also change the lock's current node if they return true. They use the same criteria as parent() and child(), for the return value, and the lock's current position is unchanged with the false return value.

For the two-argument version of to_child(), if the second parameter is true, the lock gets repositioned to the specified inferior child node, if it has a value, or to its closest intermediate node with a value, if to_child() returns true.