Skip to content

Commit

Permalink
Fix src/lib/support/StateMachine pattern matching
Browse files Browse the repository at this point in the history
The Enter method can rewrite the state objects during pattern matching
traversal.  We need a one-shot bool to ensure we only execute
Enter once.  And although state changes from Exit, LogTransition and
GetName aren't expected, we should check in these too.  In all cases,
the pattern match should only execute once.
  • Loading branch information
msandstedt committed Dec 22, 2021
1 parent 8ba4702 commit d163857
Showing 1 changed file with 19 additions and 13 deletions.
32 changes: 19 additions & 13 deletions src/lib/support/StateMachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,37 +48,40 @@ struct VariantState : Variant<Ts...>

private:
template <typename T>
void Enter()
void Enter(bool & ever)
{
if (chip::Variant<Ts...>::template Is<T>())
if (!ever && chip::Variant<Ts...>::template Is<T>())
{
ever = true;
chip::Variant<Ts...>::template Get<T>().Enter();
}
}

template <typename T>
void Exit()
void Exit(bool & ever)
{
if (chip::Variant<Ts...>::template Is<T>())
if (!ever && chip::Variant<Ts...>::template Is<T>())
{
ever = true;
chip::Variant<Ts...>::template Get<T>().Exit();
}
}

template <typename T>
void GetName(const char ** name)
{
if (name && chip::Variant<Ts...>::template Is<T>())
if (name && !*name && chip::Variant<Ts...>::template Is<T>())
{
*name = chip::Variant<Ts...>::template Get<T>().GetName();
}
}

template <typename T>
void LogTransition(const char * previous)
void LogTransition(bool & ever, const char * previous)
{
if (chip::Variant<Ts...>::template Is<T>())
if (!ever && chip::Variant<Ts...>::template Is<T>())
{
ever = true;
chip::Variant<Ts...>::template Get<T>().LogTransition(previous);
}
}
Expand All @@ -94,12 +97,14 @@ struct VariantState : Variant<Ts...>

void Enter()
{
[](...) {}((this->template Enter<Ts>(), 0)...);
bool ever = false;
[](...) {}((this->template Enter<Ts>(ever), 0)...);
}

void Exit()
{
[](...) {}((this->template Exit<Ts>(), 0)...);
bool ever = false;
[](...) {}((this->template Exit<Ts>(ever), 0)...);
}

const char * GetName()
Expand All @@ -111,7 +116,8 @@ struct VariantState : Variant<Ts...>

void LogTransition(const char * previous)
{
[](...) {}((this->template LogTransition<Ts>(previous), 0)...);
bool ever = false;
[](...) {}((this->template LogTransition<Ts>(ever, previous), 0)...);
}
};

Expand Down Expand Up @@ -215,10 +221,10 @@ class StateMachine : public Context<TEvent>
auto newState = mTransitions(mCurrentState, evt);
if (newState.HasValue())
{
auto oldState = mCurrentState;
oldState.Exit();
auto oldState = mCurrentState.GetName();
mCurrentState.Exit();
mCurrentState = newState.Value();
mCurrentState.LogTransition(oldState.GetName());
mCurrentState.LogTransition(oldState);
// It is impermissible to dispatch events from Exit() or
// LogTransition(), or from the transitions table when a transition
// has also been returned. Verify that this hasn't occurred.
Expand Down

0 comments on commit d163857

Please sign in to comment.