Index
Methods described in Chapter 8, Synchronous API send a message to the client connection thread then wait for the message to get processed, before returning the results.
This is sufficient for implementing simple, basic scripts or batch jobs. For interactive applications stasher's asynchronous API might be a better fit.
Each method described in Chapter 8, Synchronous API, namely
(),
stasher::client
->get
(),
stasher::client
->put
(), and
stasher::client
->getdir
(),
has an asynchronous “stasher::client
->subscribename
_request” version, that takes the
same parameters as the corresponding synchronous method.
The asynchronous “name
_request” returns a
std::pair
.
A std::pair
returned by
“name
_request”
has stasher::
for its name
requestfirst
, and a
stasher::client::base:request
.
This is a consistent naming convention. For example,
get_request
() returns a
std::pair<stasher::getrequest, stasher::client::base::request>
,
put_request
() returns a
std::pair<stasher::putrequest, stasher::client::base::request>
,
and so on.
The asynchronous “name
_request” method does not wait for the
request to be processed, it returns immediately.
std::pair
's second
,
a stasher::client::base:request
, is an
x::ref
to an object that indicates the status of
the request, using the following methods:
stasher::client::base:request
->done
()
done
() returns true
when the request is processed, and the return value in
std::pair
's first
is
valid.
stasher::client::base:request
->wait
()
If the requedt is not processed,
wait
() waits until it does. After
wait
() returns,
done
() always returns
true
.
stasher::client::base:request
->mcguffin
()
mcguffin
() returns a reference to an
opaque object that represents the request's mcguffin. The client connection thread
has an internal reference on the underlying object, which gets
released when the client connection thread processes the request. If no other strong
references to the object remain, it goes out of scope and gets
destroyed, invoking any destructor callbacks.
The callbacks get invoked by the client connection thread. They should not throw exceptions, block, or take an excessive time to return.
The stasher::
in
name
requeststd::pair
's first
,
contains a single getmsg
() method that returns
the results of the request, the same return value that the
synchronous
()
would return.
stasher's synchronous API is just a wrapper for the
asynchronous API. Here's all of
name
():
stasher::client
->get
stasher::getresults stasher::clientObj::get(const stasher::getreq &contents_req) { std::pair<stasher::getrequest, stasher::client::base::request> req=get_request(contents_req); req.second->wait(); return req.first->getmsg(); }
All other synchronous methods work the same way.
As an alternative to wait
(), use
mcguffin
() and attach a destructor callback to it,
which gets invoked when the request completes.
For consistent semantics, after the request is already done
mcguffin
() returns a dummy mcguffin, without
retaining a reference on it. When the caller attaches a destructor
callback to it, afer the dummy mcguffin goes out of scope and gets
destroyed, the destructor callback gets triggered, since the request
has completed, already.
Note that, as mentioned previously, destructor callacks usually get invoked by the client connection thread. They should not throw exceptions, or do something that takes a while.
Callbacks that get executed by a client connection thread cannot invoke synchronous C++ methods, but they can invoke asynchronous methods, since the asynchronous methods simply send a message to the client connection thread and return. Synchronous methods send the message and wait for a response from the threads, so a thread deadlock occurs when the same thread invokes the synchronous method.
Note that this applies to the
updated
()
callback that gets invoked by
,
and to destructor callbacks attached to asynchronous requests'
mcguffins, since they're usually invoked by the client connection thread.
stasher::client
->subscribe
In general, callback objects must not have a strong reference on
stasher::client
. There are at least two reasons for
that.
In most cases, the client connection thread thread
has a strong reference on the callback object. If that object, in turn
has a stasher::client
, this creates a circular
reference.
stasher::client
is an
x::ref
itself.
When the last reference to the underlying object gets destroyed, the
underlying object's destructor stops the client connection thread thread if it's
running,
disconnecting from the object repository. The circular reference
prevents this from happening.
Using a weak reference is usually insufficient, since a strong
reference gets recovered from the weak one, whenever the weak
reference needs to be used in any way. Setting aside the fact
that a circular reference exists now exists, if all other instances
of stasher::client
referring to this
connection go out of scope and get destroyed, this one becomes the
last stasher::client
, and when it goes out of
scope and gets destroyed, the underlying object's destructor attempts
to stop the client connection thread, and waits until it does.
If this happens during one of the callbacks which gets executed by the client connection thread, this results in a thread deadlock, since the client connection thread is essentially waits for itself to stop.
The recommended approach is for the callbacks to do little beyond sending
a message to another thread, or recording their invocation in some queue
or an intermediate object, of some sort. Other threads or objects with
their own stasher::client
can have a reference to the
same intermediate queue or object, without creating a circular reference.
Under very controlled situations, it's possible for a callback to have
its own stasher::client
. See
the section called “
(): asynchronous version of stasher::client
->get_requestget
()” for an example.