Free Electron
Using a StateCatalog

Accessing a StateCatalog

You can access a named space from the fe::ext::NetHost, in the form of an fe::StateCatalog.

sp<StateCatalog> spStateCatalog=spNetHost->accessSpace("world",implementation);

If there is already a StateCatalog for a name, this will return the same instance. Otherwise, a new StateCatalog is created. The optional second argument specifies a particular implementation in dot form (see Component Naming).

Preparing a StateCatalog

A StateCatalog implementation will presumably need some configuration. Once configured, you should start the instance, which could, for example, initialiate network connections.

Result result=spStateCatalog->configure("localhost:7890 role=client");
result=spStateCatalog->start();

The configure() method is a shorthand for specifying many values, which could have been specified one at a time. See the Detailed Description in fe::ext::ConnectedCatalog.

Many of the StateCatalog methods return a Result value. Error checking is not shown in these examples, but it is wise to check for success with fe::successful(result) or fe::failure(result).

If you don't want the caller to proceed as an isolated node, you can wait for a connection.

spStateCatalog->waitForConnection();

When you are done using a StateCatalog, you can stop the connection.

spStateCatalog->stop();

The destructor for a StateCatalog implementation may stop the connection automatically.

Setting State

The state in the StateCatalog can be changed with the template method setState(). A set of changes is sent along during a flush. The recipient can recognize the collective update of state changes sent by a single flush (all the changes since the previous flush, or from start, for the first flush).

result=spStateCatalog->setState<Real>("myReal",1);
result=spStateCatalog->setState<String>("myText","Hello");
result=spStateCatalog->setState<String>("myText","hint","greeting");
spStateCatalog->flush();

The StateCatalog will be locked during each individual setState() call, as well as the flush().

Your particular domain will probably have a list and/or set of rules about what key names you can use. The key prefix of "net:" is reserved for connection data.

While the compiler can usually guess the template type based on arguments, that is generally unsafe as it will often choose the wrong type. In this case, '1' would be an int and "Hello" would be a char*, which was not our intended storage types.

While state changes are often specified as just key-value pairs, they are actually key-property-value triplets, where the default property is "value". The example shows a "hint" property for the "myText" key. There may be some reserved properties, like "local" and "message", but generally you can use any properties you like.

Getting State

The state in the StateCatalog can be directly read with the template method getState().

String myText;
result=spStateCatalog->getState<String>("myText",myText);

The StateCatalog will be locked during each individual getState() call.

Direct access like this makes no effort to associate collective changes. You can synchronize access to the senders flush calls using an Atomic or Snapshot.

Getting State with Atomic

An fe::StateCatalog::Atomic instance will lock the StateCatalog for the scope of that instance. Access to state through the Atomic is synchronized with the latest incoming flush.

{
StateCatalog::Atomic atomic(spStateCatalog);
Real myReal(0);
result=atomic.getState<Real>("myReal",myReal);
String myText;
result=atomic.getState<String>("myText",myText);
}

You can specify an optional second argument to the Atomic constructor with a referenced integer that specifies the previous flush count. That referenced value will then get set to the next flush count. This usage will block until the current flush count exceeds the flush count value you provided.

An optional third argument to the Atomic constructor specifies a pause in microseconds after each negative check that the current flush count has exceeded the argument's flush count.

An optional fourth argument to the Atomic constructor specifies a volatile BWORD (boolean) that will be checked in the wait loop. If that boolean ever becomes zero, You can check Atomic::locked() to see whether the lock was successful.

Warning
Since the Atomic is holding a lock, you should ensure that it falls out of scope as soon as possible.

Getting State with a Snapshot

For a persistent view of instantaneous state synchronized to an incoming flush, an fe::StateCatalog::Snapshot can make a lightweight sparse copy. Once the copy is made, no locks are retained.

sp<StateCatalog::Snapshot> spSnapshot;
result=spStateCatalog->snapshot(spSnapshot);
Real myReal(0);
spSnapshot->getState<Real>("myReal",myReal);
String myText;
spSnapshot->getState<String>("myText",myText);

This will get the latest Snapshot, even if it the same as a previous Snapshot you have gotten (same serial number).

If you want to wait for a change in state, you can check the flush count and/or serial number. The serial number will increment if the flush count increments or if a local change has occured (such as a setState() on the same process).

I32 flushCount(0);
while(TRUE)
{
const I32 lastFlushCount=flushCount;
sp<StateCatalog::Snapshot> spSnapshot;
while(flushCount==lastFlushCount)
{
result=spStateCatalog->snapshot(spSnapshot);
flushCount=spSnapshot->flushCount();
}
Real myReal(0);
spSnapshot->getState<Real>("myReal",myReal);
String myText;
spSnapshot->getState<String>("myText",myText);
}

Sending Messages

Generally, incoming state clobbers previous state. However, messages are queued up on the sending side, deleted as they are sent, and then queued up on the receiver. If there are no network losses, all messages will be made available to the recipient.

Messages are sent with a special method similar to setState(). Messages can not be sent on a particular property.

spStateCatalog->sendMessage<String>("request","send help");

The receiver can pop messages off the incoming queue, first in, first out.

String message;
result=spStateCatalog->nextMessage("request",message);

The Result code will indicate failure if there were no messages.

Note
Only String messages are currently supported.

Sending Signals Remotely

An alternative to sending simple messages of one type is to send a formatted fe::Record remotely as a signal.

The originator of the signals must first set up an fe::ext::SignalMessenger. The SignalMessenger will handle some set of local signals and then send a copy of each one over the connection.

First, the SignalMessenger is created and configured.

sp<HandlerI> spSignalMessenger=spRegistry->create("*.SignalMessenger");
sp<StateBindI> spStateBindI(spSignalMessenger);
if(spStateBindI.isValid())
{
spStateBindI->bind(spStateCatalog);
spStateBindI->setKey("my_signal");
}

The key is the name to use in the given spStateCatalog.

The SignalMessenger can then be inserted to any general fe::ext::SignalerI.

spSignalerI->insert(spSignalMessenger,spLayout);

If the spLayout argument is sp<Layout>(NULL), the SignalMessenger will relay all the records that the Signaler signals. Otherwise, it relays every signal of the given fe::Layout.

Note
The remote signaling uses the sendMessage() mechanism, so the signals will likewise queue up on the sending side and clear out as they are actually sent.

Receiving Remote Signals

Each receiver of remote signals must first set up an fe::ext::MessageSignaler.

sp<SignalerI> spSignalerI=spRegistry->create("*.MessageSignaler");
spStateCatalog->addListener(spSignalerI,"my_signal");

The MessageSignaler is an fe::ext::SignalerI like any other, but it generates its signals by listening for changes on a StateCatalog.

You can insert arbitrary fe::ext::HandlerI implementations into the MessageSignaler. Those handlers' handle() methods will be called whenever the MessageSignaler is notified of an appropriate change in the StateCatalog.

Note
The remote signaling uses the nextMessage() mechanism, so unless there is dropped data in the connection, all signals will be queued up and propagated in order.

Next Step

Often, a StateCatalog will load an initial state from YAML files.

For more, see Loading YAML into a Catalog .