{"id":21274163,"url":"https://github.com/endurodave/statemachinewiththreads","last_synced_at":"2025-07-11T06:33:50.828Z","repository":{"id":44377824,"uuid":"108658881","full_name":"endurodave/StateMachineWithThreads","owner":"endurodave","description":"C++ State Machine with Asynchronous Callbacks","archived":false,"fork":false,"pushed_at":"2024-11-13T14:17:19.000Z","size":244,"stargazers_count":15,"open_issues_count":0,"forks_count":3,"subscribers_count":0,"default_branch":"master","last_synced_at":"2024-11-13T15:25:44.035Z","etag":null,"topics":["asynchronous-programming","cpp","publish-subscribe","state-machine"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/endurodave.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-10-28T15:05:17.000Z","updated_at":"2024-11-13T14:17:23.000Z","dependencies_parsed_at":"2023-01-31T20:16:21.790Z","dependency_job_id":null,"html_url":"https://github.com/endurodave/StateMachineWithThreads","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FStateMachineWithThreads","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FStateMachineWithThreads/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FStateMachineWithThreads/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FStateMachineWithThreads/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/endurodave","download_url":"https://codeload.github.com/endurodave/StateMachineWithThreads/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225701549,"owners_count":17510555,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asynchronous-programming","cpp","publish-subscribe","state-machine"],"created_at":"2024-11-21T09:19:11.943Z","updated_at":"2025-07-11T06:33:50.822Z","avatar_url":"https://github.com/endurodave.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"![License MIT](https://img.shields.io/github/license/BehaviorTree/BehaviorTree.CPP?color=blue)\n[![conan Ubuntu](https://github.com/endurodave/StateMachineWithThreads/actions/workflows/cmake_ubuntu.yml/badge.svg)](https://github.com/endurodave/StateMachineWithThreads/actions/workflows/cmake_ubuntu.yml)\n[![conan Ubuntu](https://github.com/endurodave/StateMachineWithThreads/actions/workflows/cmake_clang.yml/badge.svg)](https://github.com/endurodave/StateMachineWithThreads/actions/workflows/cmake_clang.yml)\n[![conan Windows](https://github.com/endurodave/StateMachineWithThreads/actions/workflows/cmake_windows.yml/badge.svg)](https://github.com/endurodave/StateMachineWithThreads/actions/workflows/cmake_windows.yml)\n\n# C++ State Machine with Threads\n\nA framework combining C++ state machines and multicast asynchronous callbacks.\n\nOriginally published on CodeProject at: \u003ca href=\"https://www.codeproject.com/Articles/1156423/Cplusplus-State-Machine-with-Threads\"\u003e\u003cstrong\u003eC++ State Machine with Threads\u003c/strong\u003e\u003c/a\u003e\n\n# Table of Contents\n\n- [C++ State Machine with Threads](#c-state-machine-with-threads)\n- [Table of Contents](#table-of-contents)\n- [Getting Started](#getting-started)\n- [Introduction](#introduction)\n  - [Alternate Implementations](#alternate-implementations)\n- [Self-Test Subsystem](#self-test-subsystem)\n- [Asynchronous Callbacks](#asynchronous-callbacks)\n  - [SelfTestEngine](#selftestengine)\n  - [CentrifugeTest](#centrifugetest)\n  - [Timer](#timer)\n- [Poll Events](#poll-events)\n- [Connecting Callbacks to Event Functions](#connecting-callbacks-to-event-functions)\n- [User Interface](#user-interface)\n- [Run-Time](#run-time)\n- [References](#references)\n- [Conclusion](#conclusion)\n\n# Getting Started\n\nTo build and run the project, follow these simple steps. The library uses \u003ca href=\"https://www.cmake.org\"\u003eCMake\u003c/a\u003e to generate build files and supports Visual Studio, GCC, and Clang toolchains.\n\n1. Clone the repository.\n2. From the repository root, run the following CMake command:   \n   `cmake -B Build .`\n3. Build and run the project within the `Build` directory. \n\n# Introduction\n\n\u003cp\u003eA software-based Finite State Machines (FSM) is an implementation method used to decompose a design into states and events. Simple embedded devices with no operating system employ single threading such that the state machines run on a single \u0026ldquo;thread\u0026rdquo;. More complex systems use multithreading to divvy up the processing.\u003c/p\u003e\n\n\u003cp\u003eMany FSM implementations exist including my repository \u0026ldquo;\u003cstrong\u003e\u003ca href=\"https://github.com/endurodave/StateMachine\"\u003eState Machine Design in C++\u003c/a\u003e\u003c/strong\u003e\u0026rdquo;. The article covers how to create C++ state machines using the \u003ccode\u003eStateMachine\u003c/code\u003e base class. What is missing, however, is how to integrate multiple state machines into the context of a multithreaded environment.\u003c/p\u003e\n\n\u003cp\u003e\u0026ldquo;\u003cstrong\u003e\u003ca href=\"https://github.com/endurodave/AsyncCallback\"\u003eAsynchronous Multicast Callbacks with Inter-Thread Messaging\u003c/a\u003e\u003c/strong\u003e\u0026rdquo; is another repository. This design provides a simple, portable callback mechanism that handles the low-level details of asynchronously invoking a callback with event data on a client-specified thread of control.\u003c/p\u003e\n\n\u003cp\u003eThis article combines the two previously described techniques, state machines and asynchronous callbacks, into a single project. In the previous articles, it may not be readily apparent using simple examples how multiple state machines coordinate activities and dispatch events to each other. The goal for the article is to provide a complete working project with threads, timers, events, and state machines all working together. To illustrate the concept, the example project implements a state-based self-test engine utilizing asynchronous communication between threads.\u003c/p\u003e\n\n\u003cp\u003eI won\u0026rsquo;t be re-explaining the \u003ccode\u003eStateMachine \u003c/code\u003eand \u003ccode\u003eAsyncCallback\u0026lt;\u0026gt;\u003c/code\u003e implementations as the prior articles do that already. The primary focus is on how to combine the state machine and asynchronous callbacks into a single framework.\u003c/p\u003e\n\n## Alternate Implementations\n\nAlternative C and C++ state machines libraries combined with OS threads.\n\n* \u003ca href=\"https://github.com/endurodave/C_StateMachineWithThreads\"\u003eC Language State Machine with Threads\u003c/a\u003e - A C language state machine using asynchronous callbacks.\n* \u003ca href=\"https://github.com/endurodave/StateMachineWithModernDelegates\"\u003eC++ State Machine with Asynchronous Delegates\u003c/a\u003e - A C++ state machine using asynchronous delegate callbacks.\n* \u003ca href=\"https://github.com/endurodave/AsyncStateMachine\"\u003eAsynchronous C++ State Machine\u003c/a\u003e - A C++ state machine implemented as an active object, executing in its own thread of control.\n\n# Self-Test Subsystem\n\n\u003cp\u003eSelf-tests execute a series of tests on hardware and mechanical systems to ensure correct operation. In this example, there are four state machine classes implementing our self-test subsystem as shown in the inheritance diagram below:\u003c/p\u003e\n\n\u003cp style=\"text-align: center\"\u003e\u003cimg height=\"191px\" src=\"Figure_1.png\" width=\"377px\" /\u003e\u003c/p\u003e\n\n\u003cp style=\"text-align: center\"\u003e\u003cstrong\u003eFigure 1: Self-Test Subsystem Inheritance Diagram\u003c/strong\u003e\u003c/p\u003e\n\n# Asynchronous Callbacks\n\n\u003cp\u003eThe \u003ccode\u003eAsyncCallback\u0026lt;\u0026gt;\u003c/code\u003e class is used throughout to provide asynchronous callbacks. The first place it\u0026#39;s used is within the \u003ccode\u003eSelfTest\u003c/code\u003e class. Whenever a self-test completes a \u003ccode\u003eSelfTest::CompletedCallback\u003c/code\u003e callback is invoked notifying\u0026nbsp;registered clients. \u003ccode\u003eSelfTestEngine \u003c/code\u003eregisters with both\u0026nbsp;\u003ccode\u003eCentrifugeTest \u003c/code\u003eand \u003ccode\u003ePressureTest \u003c/code\u003eto get informed when the test is complete.\u003c/p\u003e\n\n\u003cp\u003eThe second location is the user interface registers\u0026nbsp;with \u003ccode\u003eSelfTestEngine::StatusCallback\u003c/code\u003e. This allows a client, running on another thread, to register and receive status callbacks during execution. \u003ccode\u003eAsyncCallback\u0026lt;\u0026gt;\u003c/code\u003e allows the client to specify the exact callback thread making is easy to avoid cross-threading errors.\u003c/p\u003e\n\n\u003cp\u003eThe final location is within the \u003ccode\u003eTimer\u003c/code\u003e class which fires periodic callbacks on a registered callback function. A generic, low-speed timer capable of calling a function on the client-specified thread is quite useful for event driven state machines where you might want to poll for some condition to occur. In this case, the \u003ccode\u003eTimer\u003c/code\u003e class is used to inject poll events into the state machine instances.\u003c/p\u003e\n\n## SelfTestEngine\n\n\u003cp\u003e\u003ccode\u003eSelfTestEngine\u003c/code\u003e is thread-safe and the main point of contact for client\u0026rsquo;s utilizing the self-test subsystem. \u003ccode\u003eCentrifugeTest\u003c/code\u003e and \u003ccode\u003ePressureTest\u003c/code\u003e are members of \u003ccode\u003eSelfTestEngine\u003c/code\u003e. \u003ccode\u003eSelfTestEngine\u003c/code\u003e is responsible for sequencing the individual self-tests in the correct order as shown in the state diagram below. \u0026nbsp;\u003c/p\u003e\n\n\u003cp style=\"text-align: center\"\u003e\u003cimg height=\"370px\" src=\"Figure_2_1.png\" width=\"530px\" /\u003e\u003c/p\u003e\n\n\u003cp style=\"text-align: center\"\u003e\u003cstrong\u003eFigure 2: SelfTestEngine State Machine\u003c/strong\u003e\u003c/p\u003e\n\n\u003cp\u003eThe \u003ccode\u003eStart\u003c/code\u003e event initiates the self-test engine.\u0026nbsp;\u003ccode\u003e\u0026nbsp;SelfTestEngine::Start()\u003c/code\u003e is an asynchronous function relying upon \u003ccode\u003eStartCallback\u003c/code\u003e to invoke the private \u003ccode\u003eSelfTestEngine::StartPrivateCallback()\u003c/code\u003e event function. Since \u003ccode\u003eStart()\u003c/code\u003e is asynchronous, \u0026nbsp;it is thread-safe to be called by any client running on any thread.\u0026nbsp;\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nvoid SelfTestEngine::Start()\n{\n\u0026nbsp;\u0026nbsp; \u0026nbsp;// Asynchronously call StartPrivateCallback\n\u0026nbsp;\u0026nbsp; \u0026nbsp;StartCallback(NoData());\n}\n\nvoid SelfTestEngine::StartPrivateCallback()\n{\n\u0026nbsp;\u0026nbsp; \u0026nbsp;BEGIN_TRANSITION_MAP\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp; \u0026nbsp;// - Current State -\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;TRANSITION_MAP_ENTRY (ST_START_CENTRIFUGE_TEST)\u0026nbsp;\u0026nbsp;  \u0026nbsp;// ST_IDLE\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;TRANSITION_MAP_ENTRY (CANNOT_HAPPEN)\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;// ST_COMPLETED\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;TRANSITION_MAP_ENTRY (CANNOT_HAPPEN)\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;// ST_FAILED\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;TRANSITION_MAP_ENTRY (EVENT_IGNORED)\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;// ST_START_CENTRIFUGE_TEST\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;TRANSITION_MAP_ENTRY (EVENT_IGNORED)\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;// ST_START_PRESSURE_TEST\n\u0026nbsp;\u0026nbsp; \u0026nbsp;END_TRANSITION_MAP(NULL)\n}\u003c/pre\u003e\n\n\u003cp\u003eWhen each self-test completes, the \u003ccode\u003eComplete\u003c/code\u003e event fires causing the next self-test to start. After all of the tests are done, the state machine transitions to \u003ccode\u003eCompleted\u003c/code\u003e\u0026nbsp;and back to \u003ccode\u003eIdle\u003c/code\u003e. If the \u003ccode\u003eCancel\u003c/code\u003e event is generated at any time during execution, a transition to the \u003ccode\u003eFailed \u003c/code\u003estate occurs.\u003c/p\u003e\n\n\u003cp\u003eThe \u003ccode\u003eSelfTest\u003c/code\u003e base class provides three states common to all \u003ccode\u003eSelfTest\u003c/code\u003e-derived state machines: \u003ccode\u003eIdle\u003c/code\u003e, \u003ccode\u003eCompleted\u003c/code\u003e, and \u003ccode\u003eFailed\u003c/code\u003e. \u003ccode\u003eSelfTestEngine\u003c/code\u003e then adds two more states: \u003ccode\u003eStartCentrifugeTest\u003c/code\u003e and \u003ccode\u003eStartPressureTest\u003c/code\u003e.\u003c/p\u003e\n\n\u003cp\u003e\u003ccode\u003eSelfTestEngine\u003c/code\u003e has one public event function, \u003ccode\u003eStart()\u003c/code\u003e, that starts the self-tests. \u003ccode\u003eSelfTestEngine::StatusCallback\u003c/code\u003e is an asynchronous callback allowing client\u0026rsquo;s to register for status updates during testing. A \u003ccode\u003eWorkerThread\u003c/code\u003e instance is also contained within the class. All self-test state machine execution occurs on this thread.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nclass SelfTestEngine : public SelfTest\n{\npublic:\n    // Clients register for asynchronous self-test status callbacks\n    static AsyncCallback\u0026lt;SelfTestStatus\u0026gt; StatusCallback;\n\n    // Singleton instance of SelfTestEngine\n    static SelfTestEngine\u0026amp; GetInstance();\n\n    // Start the self-tests \n    void Start();\n\n    WorkerThread\u0026amp; GetThread() { return m_thread; }\n    static void InvokeStatusCallback(std::string msg);\n\nprivate:\n    AsyncCallback\u0026lt;\u0026gt; StartCallback;\n    void StartPrivateCallback();\n\n    SelfTestEngine();\n    void Complete();\n\n    // Sub self-test state machines \n    CentrifugeTest m_centrifugeTest;\n    PressureTest m_pressureTest;\n\n    // Worker thread used by all self-tests\n    WorkerThread m_thread;\n\n    // State enumeration order must match the order of state method entries\n    // in the state map.\n    enum States\n    {\n        ST_START_CENTRIFUGE_TEST = SelfTest::ST_MAX_STATES,\n        ST_START_PRESSURE_TEST,\n        ST_MAX_STATES\n    };\n\n    // Define the state machine state functions with event data type\n    STATE_DECLARE(SelfTestEngine,     StartCentrifugeTest,      NoEventData)\n    STATE_DECLARE(SelfTestEngine,     StartPressureTest,        NoEventData)\n\n    // State map to define state object order. Each state map entry defines a\n    // state object.\n    BEGIN_STATE_MAP\n        STATE_MAP_ENTRY(\u0026amp;Idle)\n        STATE_MAP_ENTRY(\u0026amp;Completed)\n        STATE_MAP_ENTRY(\u0026amp;Failed)\n        STATE_MAP_ENTRY(\u0026amp;StartCentrifugeTest)\n        STATE_MAP_ENTRY(\u0026amp;StartPressureTest)\n    END_STATE_MAP    \n\n    // Declare state machine events that receive async callbacks\n    CALLBACK_DECLARE_NO_DATA(SelfTestEngine,    StartPrivateCallback)\n    CALLBACK_DECLARE_NO_DATA(SelfTestEngine,    Complete)\n    CALLBACK_DECLARE_NO_DATA(SelfTest,          Cancel)\n};\u003c/pre\u003e\n\n\u003cp\u003eAs mentioned previously, the \u003ccode\u003eSelfTestEngine\u003c/code\u003e registers for asynchronous callbacks from each sub self-tests (i.e. \u003ccode\u003eCentrifugeTest\u003c/code\u003e and \u003ccode\u003ePressureTest\u003c/code\u003e) as shown below. When a sub self-test state machine completes, the \u003ccode\u003eSelfTestEngine::Complete()\u003c/code\u003e function is called. When a sub self-test state machine fails, the \u003ccode\u003eSelfTestEngine::Cancel()\u003c/code\u003e function is called.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nSelfTestEngine::SelfTestEngine() :\n    SelfTest(ST_MAX_STATES),\n    m_thread(\u0026quot;SelfTestEngine\u0026quot;)\n{\n    StartCallback.Register(\u0026amp;SelfTestEngine::StartPrivateCallback, \u0026amp;m_thread, this);\n\n    // Register for callbacks when sub self-test state machines complete or fail\n    m_centrifugeTest.CompletedCallback.Register(\u0026amp;SelfTestEngine::Complete, \u0026amp;m_thread, this);\n    m_centrifugeTest.FailedCallback.Register(\u0026amp;SelfTestEngine::Cancel, \u0026amp;m_thread, this);\n    m_pressureTest.CompletedCallback.Register(\u0026amp;SelfTestEngine::Complete, \u0026amp;m_thread, this);\n    m_pressureTest.FailedCallback.Register(\u0026amp;SelfTestEngine::Cancel, \u0026amp;m_thread, this);\n}\u003c/pre\u003e\n\n\u003cp\u003eThe \u003ccode\u003eSelfTest\u003c/code\u003e\u0026nbsp;base class generates the \u003ccode\u003eCompletedCallback\u003c/code\u003e and \u003ccode\u003eFailedCallback\u003c/code\u003e within the \u003ccode\u003eCompleted\u003c/code\u003e and \u003ccode\u003eFailed\u003c/code\u003e states respectively as seen below:\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nSTATE_DEFINE(SelfTest, Completed, NoEventData)\n{\n    SelfTestEngine::InvokeStatusCallback(\u0026quot;SelfTest::ST_Completed\u0026quot;);\n\n    // Generate asychronous completed callback if client is registered\n    if (CompletedCallback)\n        CompletedCallback(NoData());\n\n    InternalEvent(ST_IDLE);\n}\n\nSTATE_DEFINE(SelfTest, Failed, NoEventData)\n{\n    SelfTestEngine::InvokeStatusCallback(\u0026quot;SelfTest::ST_Failed\u0026quot;);\n\n\u0026nbsp;   // Generate asychronous failed callback if client registered\n    if (FailedCallback)\n        FailedCallback(NoData());\n\n    InternalEvent(ST_IDLE);\n}\u003c/pre\u003e\n\n\u003cp\u003eOne might ask why the state machines use asynchronous callbacks. If the state machines are on the same thread, why not use a normal, synchronous callback instead? The problem to prevent is a callback into a currently executing state machine, that is, the call stack wrapping back around into the same class instance. For example, the following call sequence should be prevented: \u003ccode\u003eSelfTestEngine\u003c/code\u003e calls \u003ccode\u003eCentrifugeTest\u003c/code\u003e calls back \u003ccode\u003eSelfTestEngine\u003c/code\u003e. An asynchronous callback allows the stack to unwind and prevents this unwanted behavior.\u003c/p\u003e\n\n## CentrifugeTest\n\n\u003cp\u003eThe \u003ccode\u003eCentrifugeTest\u003c/code\u003e state machine diagram show below implements the centrifuge self-test described in \u0026quot;\u003ca href=\"https://www.codeproject.com/Articles/1087619/State-Machine-Design-in-Cplusplus\"\u003e\u003cb\u003eState Machine Design in C++\u003c/b\u003e\u003c/a\u003e\u0026quot;. The difference here is that the \u003ccode\u003eTimer\u003c/code\u003e class is used to provide \u003ccode\u003ePoll\u003c/code\u003e events via asynchronous callbacks.\u003c/p\u003e\n\n\u003cp style=\"text-align: center\"\u003e\u003cimg height=\"769px\" src=\"Figure_3_1.png\" width=\"524px\" /\u003e\u003c/p\u003e\n\n\u003cp style=\"text-align: center\"\u003e\u003cstrong\u003eFigure 3: CentrifugeTest State Machine\u003c/strong\u003e\u003c/p\u003e\n\n## Timer\n\n\u003cp\u003eThe \u003ccode\u003eTimer\u003c/code\u003e class provides a common mechanism to receive function callbacks by registering with \u003ccode\u003eExpired\u003c/code\u003e. \u003ccode\u003eStart()\u003c/code\u003e starts the callbacks at a particular interval. \u003ccode\u003eStop()\u003c/code\u003e stops the callbacks.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nclass Timer \n{\npublic:\n    static const DWORD MS_PER_TICK;\n\n    /// An expired callback client\u0026#39;s register with to get callbacks\n    AsyncCallback\u0026lt;\u0026gt; Expired;\n\n    /// Starts a timer for callbacks on the specified timeout interval.\n    /// @param[in]    timeout - the timeout in milliseconds.\n    void Start(DWORD timeout);\n\n    /// Stops a timer.\n\u0026nbsp;   void Stop();\n\n    ...\u003c/pre\u003e\n\n\u003cp\u003eAll \u003ccode\u003eTimer\u003c/code\u003e instances are stored in a private static list. The \u003ccode\u003eWorkerThread::Process()\u003c/code\u003e loop periodically services all the timers within the list by calling \u003ccode\u003eTimer::ProcessTimers()\u003c/code\u003e. Client\u0026rsquo;s registered with \u003ccode\u003eExpired\u003c/code\u003e are invoked whenever the timer expires.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\n            case WM_USER_TIMER:\n                Timer::ProcessTimers();\n                break;\u003c/pre\u003e\n\n# Poll Events\n\n\u003cp\u003e\u003ccode\u003eCentrifugeTest\u003c/code\u003e has a \u003ccode\u003eTimer\u003c/code\u003e instance and registers for callbacks. The callback function, a thread instance and a \u003ccode\u003ethis\u003c/code\u003e pointer is provided to \u003ccode\u003eRegister()\u003c/code\u003e facilitating the asynchronous callback mechanism.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\n// Register for timer callbacks\nm_pollTimer.Expired.Register(\u0026amp;CentrifugeTest::Poll, \u0026amp;SelfTestEngine::GetInstance().GetThread(), this);\u003c/pre\u003e\n\n\u003cp\u003eWhen the timer is started using \u003ccode\u003eStart()\u003c/code\u003e, the \u003ccode\u003ePoll()\u003c/code\u003e event function is\u0026nbsp;periodically called at the interval specified.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nvoid CentrifugeTest::Poll()\n{\n    BEGIN_TRANSITION_MAP                                    // - Current State -\n        TRANSITION_MAP_ENTRY (EVENT_IGNORED)                // ST_IDLE\n        TRANSITION_MAP_ENTRY (EVENT_IGNORED)                // ST_COMPLETED\n        TRANSITION_MAP_ENTRY (EVENT_IGNORED)                // ST_FAILED\n        TRANSITION_MAP_ENTRY (EVENT_IGNORED)                // ST_START_TEST\n        TRANSITION_MAP_ENTRY (ST_WAIT_FOR_ACCELERATION)     // ST_ACCELERATION\n        TRANSITION_MAP_ENTRY (ST_WAIT_FOR_ACCELERATION)     // ST_WAIT_FOR_ACCELERATION\n        TRANSITION_MAP_ENTRY (ST_WAIT_FOR_DECELERATION)     // ST_DECELERATION\n        TRANSITION_MAP_ENTRY (ST_WAIT_FOR_DECELERATION)     // ST_WAIT_FOR_DECELERATION\n    END_TRANSITION_MAP(NULL)\n}\n\nSTATE_DEFINE(CentrifugeTest, Acceleration, NoEventData)\n{\n    SelfTestEngine::InvokeStatusCallback(\u0026quot;CentrifugeTest::ST_Acceleration\u0026quot;);\n\n    // Start polling while waiting for centrifuge to ramp up to speed\n    m_pollTimer.Start(10);\n}\u003c/pre\u003e\n\n# Connecting Callbacks to Event Functions\n\n\u003cp\u003eThe \u003ccode\u003eAsyncCallback\u0026lt;\u0026gt;\u003c/code\u003e mechanism is able to invoke a static member function or a free function. However, state machine events are implemented using instance member functions. The key to connecting the two is the \u003ccode\u003eCALLBACK_DECLARE\u003c/code\u003e and \u003ccode\u003eCALLBACK_DECLARE_NO_DATA\u003c/code\u003e macros.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eCentrifugeTest.h\u003c/strong\u003e has this\u0026nbsp;line:\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nCALLBACK_DECLARE_NO_DATA(CentrifugeTest, Poll)\u003c/pre\u003e\n\n\u003cp\u003eThe first macro argument is the class name. The second is the instance event function name. The macro is defined as:\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\n#define CALLBACK_DECLARE_NO_DATA(stateMachine, eventName) \\\n    private:\\\n    static void eventName(const NoData\u0026amp; data, void* userData) { \\\n        ASSERT_TRUE(userData != NULL); \\\n        stateMachine* stateMachine##Instance = static_cast\u0026lt;stateMachine*\u0026gt;(userData); \\\n        stateMachine##Instance-\u0026gt;eventName(); } \u003c/pre\u003e\n\n\u003cp\u003eExpanding the \u003ccode\u003eCALLBACK_DECLARE_NO_DATA\u003c/code\u003e\u0026nbsp;macro above yields:\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nprivate:\n    static void Poll(const NoData\u0026amp; data, void* userData) { \n        ASSERT_TRUE(userData != NULL); \n        CentrifugeTest* CentrifugeTestInstance = static_cast\u0026lt;CentrifugeTest*\u0026gt;(userData); \n        CentrifugeTestInstance-\u0026gt;Poll(); }\u003c/pre\u003e\n\n\u003cp\u003eThere is no magic here. A static member \u003ccode\u003ePoll()\u003c/code\u003e function is created that accepts the \u003ccode\u003eAsyncCallback\u0026lt;\u0026gt;\u003c/code\u003e callback. The \u003ccode\u003evoid* userData\u003c/code\u003e comes from 3\u003csup\u003erd\u003c/sup\u003e argument of the \u003ccode\u003eRegister()\u003c/code\u003e function and, in this case, is an instance of \u003ccode\u003eCentrifugeTest\u003c/code\u003e. The \u003ccode\u003evoid*\u003c/code\u003e is typed to \u003ccode\u003eCentrifugeTest*\u003c/code\u003e so that the instance member \u003ccode\u003ePoll()\u003c/code\u003e may be called.\u003c/p\u003e\n\n\u003cp\u003eIn this case, the \u0026ldquo;\u003ccode\u003eNO_DATA\u003c/code\u003e\u0026rdquo; macro version is used since the\u0026nbsp;\u003ccode\u003ePoll()\u003c/code\u003e\u0026nbsp;event doesn\u0026rsquo;t accept an argument. To connect a callback to an event function with\u0026nbsp;event data, the \u003ccode\u003eCALLBACK_DELCARE\u003c/code\u003e macro is used as shown below:\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nCALLBACK_DECLARE(MyStateMachine, MyEventFunc, MyEventFuncData)\u003c/pre\u003e\n\n\u003cp\u003eOf course you could do all this without the multiline macro, but it cleans up monotonous code that would otherwise be propagated throughout the project.\u003c/p\u003e\n\n# User Interface\n\n\u003cp\u003eThe project doesn\u0026rsquo;t have a user interface except the text console output. For this example, the \u0026ldquo;user interface\u0026rdquo; just outputs self-test status messages on the user interface thread via the \u003ccode\u003eSelfTestEngineStatusCallback()\u003c/code\u003e function:\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nWorkerThread userInterfaceThread(\u0026quot;UserInterface\u0026quot;);\n\nvoid SelfTestEngineStatusCallback(const SelfTestStatus\u0026amp; status, void* userData)\n{\n    // Output status message to the console \u0026quot;user interface\u0026quot;\n    cout \u0026lt;\u0026lt; status.message.c_str() \u0026lt;\u0026lt; endl;\n}\u003c/pre\u003e\n\n\u003cp\u003eBefore the self-test starts, the user interface registers with the \u003ccode\u003eSelfTestEngine::StatusCallback\u003c/code\u003e callback.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nSelfTestEngine::StatusCallback.Register(\u0026amp;SelfTestEngineStatusCallback, \u0026amp;userInterfaceThread);\u003c/pre\u003e\n\n\u003cp\u003eThe user interface thread here is just used to simulate callbacks to a GUI library normally running in a separate thread of control.\u003c/p\u003e\n\n# Run-Time\n\n\u003cp\u003eThe program\u0026rsquo;s \u003ccode\u003emain()\u003c/code\u003e function is shown below. It creates the two threads, registers for callbacks from \u003ccode\u003eSelfTestEngine\u003c/code\u003e, then calls \u003ccode\u003eStart()\u003c/code\u003e to start the self-tests.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nint main(void)\n{    \n    // Create the worker threads\n    userInterfaceThread.CreateThread();\n    SelfTestEngine::GetInstance().GetThread().CreateThread();\n\n    // Register for self-test engine callbacks\n    SelfTestEngine::StatusCallback.Register(\u0026amp;SelfTestEngineStatusCallback, \u0026amp;userInterfaceThread);\n    SelfTestEngine::GetInstance().CompletedCallback.Register(\u0026amp;SelfTestEngineCompleteCallback, \u0026amp;userInterfaceThread);\n\n    // Start the worker threads\n    ThreadWin::StartAllThreads();\n\n    // Start self-test engine\n    SelfTestEngine::GetInstance().Start();\n\n    // Wait for self-test engine to complete \n    while (!selfTestEngineCompleted)\n        Sleep(10);\n\n    // Exit the worker threads\n    userInterfaceThread.ExitThread();\n    SelfTestEngine::GetInstance().GetThread().ExitThread();\n\n    return 0;\n}\u003c/pre\u003e\n\n\u003cp\u003e\u003ccode\u003eSelfTestEngine\u003c/code\u003e generates asynchronous callbacks on the \u003ccode\u003eUserInteface\u003c/code\u003e thread. The \u003ccode\u003eSelfTestEngineStatusCallback()\u003c/code\u003e callback outputs the message to the console.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nvoid SelfTestEngineStatusCallback(const SelfTestStatus\u0026amp; status, void* userData)\n{\n    // Output status message to the console \u0026quot;user interface\u0026quot;\n    cout \u0026lt;\u0026lt; status.message.c_str() \u0026lt;\u0026lt; endl;\n}\u003c/pre\u003e\n\n\u003cp\u003eThe \u003ccode\u003eSelfTestEngineCompleteCallback()\u003c/code\u003e callback sets a flag to let the \u003ccode\u003emain()\u003c/code\u003e loop exit.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nvoid SelfTestEngineCompleteCallback(const NoData\u0026amp; data, void* userData)\n{\n    selfTestEngineCompleted = TRUE;\n}\u003c/pre\u003e\n\n\u003cp\u003eRunning the project outputs the following console messages:\u003c/p\u003e\n\n\u003cp style=\"text-align: center\"\u003e\u003cimg height=\"385px\" src=\"Figure_4.png\" width=\"628px\" /\u003e\u003c/p\u003e\n\n\u003cp style=\"text-align: center\"\u003e\u003cstrong\u003eFigure 4: Console Output\u003c/strong\u003e\u003c/p\u003e\n\n# References\n\n\u003cul\u003e\n\t\u003cli\u003e\u003cstrong\u003e\u003ca href=\"https://github.com/endurodave/StateMachine\"\u003eState Machine Design in C++\u003c/a\u003e\u003c/strong\u003e - by David Lafreniere\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003e\u003ca href=\"https://github.com/endurodave/AsyncCallback\"\u003eAsynchronous Multicast Callbacks with Inter-Thread Messaging\u003c/a\u003e\u003c/strong\u003e - by David Lafreniere\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003e\u003ca href=\"https://github.com/endurodave/C_StateMachine\"\u003eState Machine Design in C\u003c/a\u003e\u003c/strong\u003e - by David Lafreniere\u003c/li\u003e\n\u003c/ul\u003e\n\n# Conclusion\n\n\u003cp\u003eThe \u003ccode\u003eStateMachine\u003c/code\u003e and \u003ccode\u003eAsycCallback\u0026lt;\u0026gt;\u003c/code\u003e implementations can be used separately. Each is useful unto itself. However, combining the two offers a novel framework for multithreaded state-driven application development. The article has shown how to coordinate the behavior of state machines when multiple threads are used,\u0026nbsp;which may not be entirely obvious when looking at simplistic, single threaded examples.\u003c/p\u003e\n\n\u003cp\u003eI\u0026rsquo;ve successfully used ideas similar to this on many different PC and embedded projects. The code is portable to any platform with a small amount of effort. I particularly like idea of asynchronous callbacks because it effectively hides inter-thread communication and the organization of the state machines makes creating and maintaining self-tests easy.\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2Fstatemachinewiththreads","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fendurodave%2Fstatemachinewiththreads","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2Fstatemachinewiththreads/lists"}