Transient and Arbitrated Stores
Active objects do not communicate directly, instead they share information through Transient and Arbitrated stores. Conventional message-based communication between objects in disparate memory spaces usually involves the creation of a socket or similar equivalent. Generally speaking, these are expensive in terms of resources and so it is often the case that sent-messages are multiplexed to a single socket, and then demultiplexed by the receiver (typically switching on a 'type' word in the message header). This can lead to code that is difficult to browse, and in the general case, is prone to deadlocks. Latency is often an issue because the sending threads can end up blocked while the receiving threads are busy processing earlier message, or worse still, waiting for space to deposit some incoming data. This can be alleviated through the use of asynchronous communication but this is not an easy task. If stores are used, then the CORE runtime can manage all of this without the developer even being aware that the application has been accreted to multiple processes.
CDL stores can be thought of as 'topic streams'; and because their creation overhead is small, it is usual for there to be one 'store' for each message type (in the conventional sense). The data objects that are held by the stores are prototyped, and the translator will throw an error if they are connected-to inconsistently. In practice, the runtime will only create one socket between each process pair but this is transparent to the developer who can think of individual components in one process communicating with connected components in adjacent processes. Stores can be 'owned' by either communicating process (an accretion issue); but in most cases the 'server' process will create and own the store, and the 'client' process will reference it.
Transient stores are used as repositories for transient (automatic) data. Each store contains a configurable number of buffers, and readers and writers are blocked if the store is empty or full respectively. Reads are destructive, but the data is conserved until the last sharing reader completes (see Distributors). Transient store data therefore provides an equivalent to stack data (persists while referenced). See Transient Stores.
Arbitrated stores (also referred to as Persistent data stores) are used as repositories for persistent (state) data, and reads are therefore non-destructive. Arbitrated store buffers can be exclusively accessed by any number of readers, or a single writer, or a single updater. Arbitrated store data provides an equivalent to object member data and/or static data. These objects also provide an alternative to conventional data locks. See Arbitrated Stores.
CLIP provides two store objects that manage data:
- Transient Store
- Arbitrated Store
Transient Store
The resource underlying a Transient Store is a data repository that holds a pre-determined maximum number of data buffers of a given type. This object stores data transiently so that every piece of data written into the store is later read out and reading the data causes it to be destroyed. The store provides two types of event:
- Ready-for-write (a buffer is now available to be written to)
- Ready-for-read (a buffer is now available to be read from)
A typical event sequence for a transient store would be:
Click to enlarge |
|
It is important to note the distinction between data-flow and event-flow. In the sequence above, data flows from the writer to the reader. However, events flow from the store outwards to the reader and writer.
Transient stores are ideal for piping data between active objects and they guarantee that every buffer written will be received by the reader.
Arbitrated Store
The resource underlying an Arbitrated Store is a data repository that holds a pre-determined maximum number of data buffers of a given type. This object arbitrates concurrent access to persistent data and provides notifications when a given buffer changes. The store provides four types of event:
Click to enlarge |
Click to enlarge |
Click to enlarge |
Click to enlarge |
- Ready-for-write (a buffer is now available to be written to)
- Ready-for-read (a buffer is now available to be read from)
- Ready-for-update (a buffer is now available to be updated)
- Notify-updated (a buffer has just been updated or written to)
Arbitrated stores are typically updated and read from multiple active objects. The store prevents multiple updaters from accessing the underlying data object simultaneously or readers from accessing the data object while it is being written. Multiple, simultaneous readers are permitted.
These stores are used where the data must persist and remain readable for any indeterminate period (e.g. configuration data).
A common mistake is to use arbitrated stores for transient data and it is strongly recommended that you avoid this. It is possible to write/update a store from one active object and read it in another by triggering on a notify-updated event but if two updates occur in quick succession then the second write can overwrite the first before either notify-updated events are processed and so when the data is finally read the second value will be read both times, which can cause unusual errors. Transient stores are ideal for implementing the desired behavior and should always be used for this.