Chapter 22. Very ImPortant object design pattern

Index

Creating a very important object
Very important object notification callbacks
Read and write locks
An update lock
A registration handler lock
Opportunistic locking
Avoiding and debugging 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 registered event callbacks. The event 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 atomically registers a new handler, 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 event 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 handler gets the modified value, or it gets the original value of the very important object, then gets notified for 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.

The list of registered callback handlers is implemented by the x::eventcallback template, and the callbacks are reference-counted objects. x::eventcallback keeps weak references to the callback handlers. 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 invoked immediately, 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 {

public:
    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.