stasher::current<classptr
>
, stasher::currentptr<classptr
>
: a basic stasher::currentBaseObj
subclass
The following example is based on
showinventory2.C
.
This example uses
stasher::current
; this is an
x::ref
to a
stasher::currentObj
subclass that implements its update
() and
connection_update
() methods
(there's also a
stasher::currentptr
that defines an
x::ptr
):
#include <stasher/client.H> #include <stasher/manager.H> #include <stasher/current.H> #include <x/strtok.H> #include <sstream> #include <vector> #include "inventory.H" void show_inventory(const inventoryptr &, bool initial); void status(stasher::req_stat_t status); bool has_inventory(const inventoryptr &ptr, const std::string &what, int howmuch); void showinventory(int argc, char **argv) { if (argc < 2) return; auto client=stasher::client::base::connect(); auto manager=stasher::manager::create(); typedef stasher::current<inventoryptr> warehouse_t; warehouse_t warehouse=warehouse_t::create(); x::ref<x::obj> mcguffin=warehouse->manage(manager, client, argv[1]); std::cout << "Enter for the current inventory, \"what\" \"howmuch\" to wait until we have it," << std::endl << "EOF to quit" << std::endl; std::string dummy; while (!std::getline(std::cin, dummy).eof()) { std::vector<std::string> words; x::strtok_str(dummy, " \t\r\n", words); warehouse_t::base::current_value_t::lock lock(warehouse->current_value); if (words.size() == 2) { int i=-1; std::istringstream(words[1]) >> i; if (i < 0) { std::cerr << "Eh?" << std::endl; continue; } std::cout << "Waiting for " << i << " " << words[0] << std::endl; lock.wait([&lock, &words, i] { return has_inventory(lock->value, words[0], i); }); } show_inventory(lock->value, lock->isinitial); status(lock->connection_status); } } bool has_inventory(const inventoryptr &ptr, const std::string &what, int howmuch) { if (ptr.null()) { std::cout << "No inventory yet..." << std::endl; return false; } auto iter=ptr->stock.find(what); int wehave=iter == ptr->stock.end() ? 0:iter->second; std::cout << "We have " << wehave << " of them, now." << std::endl; return wehave >= howmuch; } void show_inventory(const inventoryptr &ptr, bool initial) { std::cout << (initial ? "Current inventory:":"Updated inventory:") << std::endl; if (ptr.null()) { std::cout << " (none)" << std::endl; } else { std::cout << " " << std::setw(30) << std::left << "Item" << " " << std::setw(8) << std::right << "Count" << std::setw(0) << std::endl; std::cout << " " << std::setfill('-') << std::setw(30) << "" << " " << std::setw(8) << "" << std::setw(0) << std::setfill(' ') << std::endl; for (auto &item:ptr->stock) { std::cout << " " << std::setw(30) << std::left << item.first << " " << std::setw(8) << std::right << item.second << std::setw(0) << std::endl; } std::cout << std::setw(75) << std::setfill('=') << "" << std::setw(0) << std::setfill(' ') << std::endl; } } void status(stasher::req_stat_t status) { std::cout << "Connection status: " << x::tostring(status) << std::endl; } int main(int argc, char **argv) { try { showinventory(argc, argv); } catch (const x::exception &e) { std::cerr << e << std::endl; return 1; } return 0; }
showinventory3.C
waits for Enter
before showing the current inventory. Entering “grapes 3”
before Enter waits until there are 3 grapes in the
inventory, then the current inventory gets shown:
./showinventory3 instock
Enter for the current inventory, "what" "howmuch" to wait until we have it,
EOF to quit
<Enter>
Current inventory:
Item Count
------------------------------ --------
apples 2
===========================================================================
Connection status: Transaction/request processed
grapes 3<Enter>
Waiting for 3 grapes
We have 0 of them, now.
We have 1 of them, now.
We have 3 of them, now.
Updated inventory:
Item Count
------------------------------ --------
apples 2
grapes 3
===========================================================================
Connection status: Transaction/request processed
In this example, there were two updates to the inventory in the interim. First, “grapes” was 1, then the second update set it to 3.
stasher::current<
(and
classptr
>stasher::currentptr<
)
refers to a
classptr
>stasher::currentObj<
,
a subclass of
classptr
>stasher::currentBaseObj<
.
classptr
>stasher::currentObj
contains a member called
current_value
, of type
stasher::current<
.
classptr
>::base::current_value_t
This is an x::mpcobj
,
a mutex-protected object with
value
,
a
to the
current value of the object in the repository (a classptr
nullptr
if there isn't one), a bool
isinitial
flag, indicating whether
value
is the initial object value retrieved from the
object repository, or an updated one, and a
stasher::req_stat_t
connection_status
, the latest connection status with
the stasher repository server, reported by the manager.
stasher::currentObj<
,
implements
classptr
>stasher::currentBaseObj<
's
classptr
>update
() and
connection_update
() by locking the
current_value
, updating its contents, and then
notifying the lock's condition variable, before releasing the lock.
showinventory3.C
waits for Enter,
locks the current_value
, and displays its contents.
Until the next Enter,
current_value
gets automatically updated, by the
stasher::currentObj
, when the object in the repository
changes.
To summarize:
Create a stasher::current<
, where
classptr
>
is
an classptr
x::ref
to a class
that meets the requirements described
in the section called “Requirements”, and manage it.
To check the current contents of the object in the repository,
acquire a lock on the mutex-protected
current_value
.
The lock on the current_value
should be short and
brief. If the object gets updated in the repository, a new object gets
constructed, and
stasher::currentObj
's callback gets invoked.
This all happens in the client connection thread. The client connection thread waits to acquire a lock on the
current_value
, and won't do anything else until it
does, and finishes updating the object.
The same thing happens if the connection to the server gets dropped, or
it gets reconnected. This results in the
connection_update
update getting invoked, which
also needs to acquire a lock on the
current_value
, so that the
status
can get updated.
An application that needs to take special action for an
isinitial
object value must explicitly subclass
the stasher::currentObj
, and override the
connection_update
method. This is because the
initial value of the object may or may not be installed before
manage
() returns, since the
connection_update
gets called from a different
thread. Since an object in a stasher object repository can get updated at
any time, this means that it can also get updated before
manage
() returns as well, before the application
has a chance to lock the
current_value
.
The contract for the
created managed object's
connection_update
() methods guarantees that
it gets called with a value that's marked as an initial value, but not
when.