Chapter 20. Very ImPortant object design pattern


Creating a very important object
Read and write locks
An update lock
A registration handler lock
Opportunistic locking
Optional template parameters
Avoiding deadlocks
Somewhat-ImPortant object design pattern

The x::vipobj template instantiates a class that implements a very important object design pattern. A very important object is an object paired with a list of callbacks. The callbacks get invoked when the very important object gets modified. It's a very important object, after all, and everyone wants to know when it changes, and what its new value is.

x::vipobj implements an API of atomically registers a new lambda callback, and gives it the current value of the very important object, in a single operation, as the callback's initial value. A key attribute of a very important object is that the notification mechanism must be consistent when a new lambda callback gets registered at the same time that another thread updates the very important object. In this situation, one of two things will happen here. Either the new lambda gets the modified value, or it gets the original value of the very important object, then gets notified with the modified value immediately afterwards.

These well-defined, thread-safe semantics provide a reliable access mechanism that's somewhat more sophisticated than a plain read lock and write lock design. The very important object design pattern uses four different locking objects that x::vipobj exports as part of its implementation. Access to the very important object is thread-safe, as long as these lock objects get instantiated and used as per their documented interface.

The x::vipobj template instance is not a reference-counted. object itself. A x::vipobj is typically not used directly, but is usually a member of a larger (most likely a reference-counted) class that exports a high level API. The API is implemented using x::vipobj lock objects, instantiated in the right order.

Registering a lambda with a very important object returns a x::callback handle, which should be treated as a reference-counted handle for the installed lambda. There's no formal deregistration method for a handler, just a registration mechanisms. The handler callbacks remain registered until they go out of scope and get destroyed.

When a callback gets registered, as part of registration the callback gets optionally invoked with the current value of the very important object. The callback continues to be invoked whenever the very important object is modified, with the modified value. In this manner, each callback handler maintains a stable view of the very important object's value, as it gets modified by other threads, over time, starting with the time the handler was registered with the very important object.

Creating a very important object

#include <x::vipobj.H>

class vipintvalue {

    int n;

    vipintvalue(int nValue=0);
    vipintvalue(const std::string &);

typedef x::vipobj<vipintvalue> vip_t;

vip_t vip;

x::vipobj's constructor forwards its arguments to the object's constructor.