Skip to content

Commit 126bbba

Browse files
committed
Improved handling of dynamic attach for Pulsecount and Subtree engines. It's no longer fixed to a single dynamic predecessor and doesn't grow the stack for successive shifts.
1 parent 568e249 commit 126bbba

4 files changed

Lines changed: 72 additions & 65 deletions

File tree

include/react/engine/PulsecountEngine.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ enum class ENodeState
5757
{
5858
unchanged,
5959
changed,
60-
deferred
60+
dyn_defer,
61+
dyn_repeat
6162
};
6263

6364
class Node : public IReactiveNode

include/react/engine/SubtreeEngine.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ class Node : public IReactiveNode
6767
inline void SetDeferredFlag() { flags_.Set<flag_deferred>(); }
6868
inline void ClearDeferredFlag() { flags_.Clear<flag_deferred>(); }
6969

70+
inline bool IsRepeated() const { return flags_.Test<flag_repeated>(); }
71+
inline void SetRepeatedFlag() { flags_.Set<flag_repeated>(); }
72+
inline void ClearRepeatedFlag() { flags_.Clear<flag_repeated>(); }
73+
7074
inline bool IsInitial() const { return flags_.Test<flag_initial>(); }
7175
inline void SetInitialFlag() { flags_.Set<flag_initial>(); }
7276
inline void ClearInitialFlag() { flags_.Clear<flag_initial>(); }
@@ -96,9 +100,9 @@ class Node : public IReactiveNode
96100

97101
NodeVector<Node> Successors;
98102
ShiftMutexT ShiftMutex;
99-
uint16_t Level { 0 };
100-
uint16_t NewLevel { 0 };
101-
uint16_t WaitCount { 0 };
103+
uint16_t Level = 0;
104+
uint16_t NewLevel = 0;
105+
uint16_t WaitCount = 0;
102106

103107
private:
104108
enum EFlags : uint16_t
@@ -107,6 +111,7 @@ class Node : public IReactiveNode
107111
flag_marked,
108112
flag_changed,
109113
flag_deferred,
114+
flag_repeated,
110115
flag_initial,
111116
flag_root
112117
};
@@ -159,10 +164,10 @@ class EngineBase : public IReactiveEngine<Node,TTurn>
159164
TopoQueueT scheduledNodes_;
160165
vector<Node*> subtreeRoots_;
161166

162-
empty_task* rootTask_ { new(task::allocate_root()) empty_task };
167+
empty_task& rootTask_ = *new(task::allocate_root()) empty_task;
163168
task_list spawnList_;
164169

165-
bool isInPhase2_ { false };
170+
bool isInPhase2_ = false;
166171
};
167172

168173
class BasicEngine : public EngineBase<Turn> {};

src/engine/PulsecountEngine.cpp

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,16 @@ class UpdaterTask: public task
112112
if (node.Mark() == ENodeMark::should_update)
113113
node.Tick(&turn_);
114114

115-
if (node.State == ENodeState::deferred)
115+
// Defer if node was dynamically attached to a predecessor that
116+
// has not pulsed yet
117+
if (node.State == ENodeState::dyn_defer)
116118
continue;
117119

120+
// Repeat the update if a node was dynamically attached to a
121+
// predecessor that has already pulsed
122+
while (node.State == ENodeState::dyn_repeat)
123+
node.Tick(&turn_);
124+
118125
// Mark successors for update?
119126
bool update = node.State == ENodeState::changed;
120127
node.State = ENodeState::unchanged;
@@ -253,30 +260,23 @@ void EngineBase<TTurn>::OnNodeIdlePulse(Node& node, TTurn& turn)
253260

254261
template <typename TTurn>
255262
void EngineBase<TTurn>::OnDynamicNodeAttach(Node& node, Node& parent, TTurn& turn)
256-
{
257-
bool shouldTick = false;
258-
259-
{// parent.ShiftMutex (write)
260-
NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true);
263+
{// parent.ShiftMutex (write)
264+
NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true);
261265

262-
parent.Successors.Add(node);
263-
264-
// Has already nudged its neighbors?
265-
if (parent.Mark() == ENodeMark::unmarked)
266-
{
267-
shouldTick = true;
268-
}
269-
else
270-
{
271-
node.State = ENodeState::deferred;
272-
node.SetCounter(1);
273-
node.SetMark(ENodeMark::should_update);
274-
}
275-
}// ~parent.ShiftMutex (write)
266+
parent.Successors.Add(node);
276267

277-
if (shouldTick)
278-
node.Tick(&turn);
279-
}
268+
// Has already nudged its neighbors?
269+
if (parent.Mark() == ENodeMark::unmarked)
270+
{
271+
node.State = ENodeState::dyn_repeat;
272+
}
273+
else
274+
{
275+
node.State = ENodeState::dyn_defer;
276+
node.IncCounter();
277+
node.SetMark(ENodeMark::should_update);
278+
}
279+
}// ~parent.ShiftMutex (write)
280280

281281
template <typename TTurn>
282282
void EngineBase<TTurn>::OnDynamicNodeDetach(Node& node, Node& parent, TTurn& turn)

src/engine/SubtreeEngine.cpp

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,24 @@ class UpdaterTask: public task
8585
node.ClearInitialFlag();
8686
node.SetShouldUpdate(false);
8787

88-
// Skip deferred node
88+
// Defer if node was dynamically attached to a predecessor that
89+
// has not pulsed yet
8990
if (node.IsDeferred())
9091
{
9192
node.ClearDeferredFlag();
9293
continue;
9394
}
9495

96+
// Repeat the update if a node was dynamically attached to a
97+
// predecessor that has already pulsed
98+
while (node.IsRepeated())
99+
{
100+
node.ClearRepeatedFlag();
101+
node.Tick(&turn_);
102+
}
103+
104+
node.SetReadyCount(0);
105+
95106
// Mark successors for update?
96107
bool update = node.IsChanged();
97108

@@ -107,8 +118,6 @@ class UpdaterTask: public task
107118
if (succ->IncReadyCount())
108119
continue;
109120

110-
succ->SetReadyCount(0);
111-
112121
// Heavyweight - spawn new task
113122
if (succ->IsHeavyweight())
114123
{
@@ -124,11 +133,11 @@ class UpdaterTask: public task
124133

125134
if (nodes_.IsFull())
126135
{
127-
splitCount++;
136+
++splitCount;
128137

129138
//Delegate half the work to new task
130139
auto& t = *new(task::allocate_additional_child_of(*parent()))
131-
UpdaterTask(*this, SplitTag{});
140+
UpdaterTask(*this, SplitTag());
132141

133142
spawn(t);
134143
}
@@ -171,24 +180,24 @@ void EngineBase<TTurn>::Propagate(TTurn& turn)
171180
// Phase 2
172181
isInPhase2_ = true;
173182

174-
rootTask_->set_ref_count(1 + subtreeRoots_.size());
183+
rootTask_.set_ref_count(1 + subtreeRoots_.size());
175184

176185
for (auto* node : subtreeRoots_)
177186
{
178187
// Ignore if root flag has been cleared because node was part of another subtree
179188
if (! node->IsRoot())
180189
{
181-
rootTask_->decrement_ref_count();
190+
rootTask_.decrement_ref_count();
182191
continue;
183192
}
184193

185-
spawnList_.push_back(*new(rootTask_->allocate_child())
194+
spawnList_.push_back(*new(rootTask_.allocate_child())
186195
UpdaterTask<TTurn>(turn, node));
187196

188197
node->ClearRootFlag();
189198
}
190199

191-
rootTask_->spawn_and_wait_for_all(spawnList_);
200+
rootTask_.spawn_and_wait_for_all(spawnList_);
192201

193202
subtreeRoots_.clear();
194203
spawnList_.clear();
@@ -242,36 +251,28 @@ void EngineBase<TTurn>::OnDynamicNodeDetach(Node& node, Node& parent, TTurn& tur
242251

243252
template <typename TTurn>
244253
void EngineBase<TTurn>::applyAsyncDynamicAttach(Node& node, Node& parent, TTurn& turn)
245-
{
246-
bool shouldTick = false;
247-
248-
{// parent.ShiftMutex (write)
249-
NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true);
254+
{// parent.ShiftMutex (write)
255+
NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true);
250256

251-
parent.Successors.Add(node);
252-
253-
// Level recalulation applied when added to topoqueue next time.
254-
// During the async phase 2 it's not needed.
255-
if (node.NewLevel <= parent.Level)
256-
node.NewLevel = parent.Level + 1;
257-
258-
// Has already nudged its neighbors?
259-
if (! parent.IsMarked())
260-
{
261-
shouldTick = true;
262-
}
263-
else
264-
{
265-
node.SetDeferredFlag();
266-
node.SetShouldUpdate(true);
257+
parent.Successors.Add(node);
267258

268-
node.WaitCount = 1;
269-
}
270-
}// ~parent.ShiftMutex (write)
259+
// Level recalulation applied when added to topoqueue next time.
260+
// During the async phase 2 it's not needed.
261+
if (node.NewLevel <= parent.Level)
262+
node.NewLevel = parent.Level + 1;
271263

272-
if (shouldTick)
273-
node.Tick(&turn);
274-
}
264+
// Has already nudged its neighbors?
265+
if (! parent.IsMarked())
266+
{
267+
node.SetRepeatedFlag();
268+
}
269+
else
270+
{
271+
node.SetDeferredFlag();
272+
node.SetShouldUpdate(true);
273+
node.DecReadyCount();
274+
}
275+
}// ~parent.ShiftMutex (write)
275276

276277
template <typename TTurn>
277278
void EngineBase<TTurn>::applyAsyncDynamicDetach(Node& node, Node& parent, TTurn& turn)

0 commit comments

Comments
 (0)