Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/STATE_EXEC_DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1372,7 +1372,8 @@ created (e.g. via `state-set` or `cvc::state::operator()(path).value(v)`),
the internal sequence is:

1. `valueChanged()` fires on the new node — **no observer yet**.
2. `childChanged()` propagates up from parent to root.
2. `childChanged()` fires on the parent and on every ancestor above it,
the root's slots first (`state::emitChildChanged()`).
3. The adapter's `childChanged` handler attaches an observer on the
new node.

Expand Down
4 changes: 3 additions & 1 deletion docs/roadmap/STATE_LIFETIME_AND_ATOMICITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ leaf-pin does not keep alive:
- `state_resolve` computes `"state://" + eff->fullName() + …` on **every** resolve
(`uri_state.cpp:64`; `state_store` at `:97`). `fullName()` → `parentName()`
(`state.h:227-239`) dereferences `_parent` (a raw `const state*`, `state.h:747`),
recursing up the chain.
recursing up the chain. (They are loops up the chain now, and so is the
`childChanged` notification of the ancestors, `state::emitChildChanged()`; the
`_parent` dereferences, and this hazard, are unchanged.)
- `effective_node`/`resolveLink` also call `fullName()` on the start and target
nodes (`state.cpp:1016,1047`).

Expand Down
44 changes: 23 additions & 21 deletions inc/cvc/state/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,20 +232,13 @@ class state : public boost::enable_shared_from_this<state> {
const std::string &name() const { return _name; }
const state *parent() const { return _parent; }

// return's the parent's fullName
std::string parentName() const {
std::string tmp;
return parent() ? (!parent()->name().empty() ? ((tmp = parent()->parentName()).empty()
? parent()->name()
: tmp + SEPARATOR + parent()->name())
: "")
: "";
}
// The parent's full name: the names of the ancestors from just below the first one with an
// empty name (normally the root) down to the parent, joined with SEPARATOR; "" when there are
// none. Built in one pass over the _parent chain, not once per level.
std::string parentName() const;

std::string fullName() const {
std::string pn = parentName();
return pn.empty() ? name() : pn + SEPARATOR + name();
}
// This node's path from the root: parentName(), then its own name.
std::string fullName() const;

boost::posix_time::ptime lastMod();

Expand Down Expand Up @@ -275,9 +268,6 @@ class state : public boost::enable_shared_from_this<state> {
template <class T> state &value(const T &v) {
std::string str_value = boost::lexical_cast<std::string>(v);

// Check if read-only before acquiring lock
std::string full_name = fullName();

// Phase 8: writes through a writable transparent link route to the
// resolved target, mirroring the string overload
// (state::value(const std::string&, bool) in state.cpp). Routing to the
Expand Down Expand Up @@ -305,7 +295,7 @@ class state : public boost::enable_shared_from_this<state> {
return *this;
}
throw read_only_error(boost::str(
boost::format("Cannot write through unresolvable transparent link: %1%") % full_name));
boost::format("Cannot write through unresolvable transparent link: %1%") % fullName()));
}
}

Expand All @@ -315,7 +305,7 @@ class state : public boost::enable_shared_from_this<state> {
// Check if this state is read-only
if (_readOnly) {
throw read_only_error(
boost::str(boost::format("Cannot modify read-only state: %1%") % full_name));
boost::str(boost::format("Cannot modify read-only state: %1%") % fullName()));
}

if (_value == str_value)
Expand All @@ -331,7 +321,7 @@ class state : public boost::enable_shared_from_this<state> {

valueChanged();
if (parent())
parent()->childChanged(name());
parent()->emitChildChanged(name());
return *this;
}

Expand Down Expand Up @@ -422,9 +412,23 @@ class state : public boost::enable_shared_from_this<state> {
signal dataChanged;

state &operator()(const std::string &childname = std::string());
// The full names of every node under this one (its descendants, in pre-order: each child, then
// its own descendants, children in name order), those matching `re` when it is not empty.
std::vector<std::string> children(const std::string &re = std::string());
size_t numChildren();

// Fired on a node when something under it changes, with that thing's path relative to the
// node. A change signals the changed node's parent and every ancestor above it, the root's
// slots first (emitChildChanged()). Emitting childChanged directly fires only that node's
// slots.
map_change_signal childChanged;

// Fire childChanged for `childname` (a path relative to this node) on this node and on each
// ancestor, with the path relative to that ancestor: the root's slots first, this node's last.
// That is the order the old relay (each node's first childChanged slot re-firing its parent's)
// produced, but this is a loop, so a deep tree costs no stack per level, and a path is built
// only for a node that has slots.
void emitChildChanged(const std::string &childname) const;
operator std::string() { return value(); }

signal destroyed;
Expand Down Expand Up @@ -805,8 +809,6 @@ class state : public boost::enable_shared_from_this<state> {
protected:
state(app &ctx, const std::string &n = std::string(), const state *p = NULL);

void notifyParent(const std::string &childname);

app &_ctx;
boost::mutex _mutex;
boost::posix_time::ptime _lastMod;
Expand Down
Loading
Loading