{"id":21274165,"url":"https://github.com/endurodave/statemachinewithdelegates","last_synced_at":"2025-07-11T06:33:51.467Z","repository":{"id":113549871,"uuid":"108659720","full_name":"endurodave/StateMachineWithDelegates","owner":"endurodave","description":"C++ State Machine with Delegates","archived":false,"fork":false,"pushed_at":"2024-11-13T14:14:46.000Z","size":300,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"master","last_synced_at":"2024-11-13T15:24:56.543Z","etag":null,"topics":["asynchronous-callbacks","cpp","delegates","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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-28T15:16:03.000Z","updated_at":"2024-11-13T14:14:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"16ab5e3e-b688-4cf7-bae4-87f6fbe827c3","html_url":"https://github.com/endurodave/StateMachineWithDelegates","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%2FStateMachineWithDelegates","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FStateMachineWithDelegates/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FStateMachineWithDelegates/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FStateMachineWithDelegates/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/endurodave","download_url":"https://codeload.github.com/endurodave/StateMachineWithDelegates/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-callbacks","cpp","delegates","state-machine"],"created_at":"2024-11-21T09:19:12.110Z","updated_at":"2025-07-11T06:33:51.461Z","avatar_url":"https://github.com/endurodave.png","language":"C++","readme":"# C++ State Machine with Asynchronous Multicast Delegates\nA framework combining C++ state machines with asynchronous multicast delegates.\n\nOriginally published on CodeProject at: \u003ca href=\"https://www.codeproject.com/Articles/1165243/Cplusplus-State-Machine-with-Asynchronous-Multicas\"\u003eC++ State Machine with Asynchronous Multicast Delegates\u003c/a\u003e\n\n## ⚠️ New Repository\n\nSee the updated implementation of this code at: \u003ca href=\"https://github.com/endurodave/StateMachineWithModernDelegates\"\u003eC++ State Machine with Modern Asynchronous Multicast Delegates\u003c/a\u003e \n\n# Table of Contents\n\n- [C++ State Machine with Asynchronous Multicast Delegates](#c-state-machine-with-asynchronous-multicast-delegates)\n  - [⚠️ New Repository](#️-new-repository)\n- [Table of Contents](#table-of-contents)\n- [Introduction](#introduction)\n- [Asynchronous Delegate Callbacks](#asynchronous-delegate-callbacks)\n- [Self-Test Subsystem](#self-test-subsystem)\n  - [SelfTestEngine](#selftestengine)\n  - [CentrifugeTest](#centrifugetest)\n  - [Timer](#timer)\n- [Win32 and std::thread Worker Threads](#win32-and-stdthread-worker-threads)\n- [Heap vs. Pool](#heap-vs-pool)\n- [Poll Events](#poll-events)\n- [User Interface](#user-interface)\n- [Run-Time](#run-time)\n- [Conclusion](#conclusion)\n- [References](#references)\n\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 one I wrote about here on Code Project entitled \u0026ldquo;\u003ca href=\"http://www.codeproject.com/Articles/1087619/State-Machine-Design-in-Cplusplus\"\u003e\u003cstrong\u003eState Machine Design in C++\u003c/strong\u003e\u003c/a\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;\u003ca href=\"https://www.codeproject.com/Articles/1160934/Asynchronous-Multicast-Delegates-in-Cplusplus\"\u003e\u003cstrong\u003eAsynchronous Multicast Delegates in C++\u003c/strong\u003e\u003c/a\u003e\u0026rdquo; is another article I wrote on Code Project. This design provides a C++ delegate library that is capable of synchronous and asynchronous invocations on any callable function.\u003c/p\u003e\n\n\u003cp\u003eThis article combines the two previously described techniques, state machines and asynchronous multicast delegates, 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\u003e and \u003ccode\u003eDelegate\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 delegates into a single framework.\u003c/p\u003e\n\n\u003cp\u003e\u003ca href=\"https://www.cmake.org/\"\u003eCMake\u003c/a\u003e\u0026nbsp;is used to create the build files. CMake is free and open-source software. Windows, Linux and other toolchains are supported. See the \u003cstrong\u003eCMakeLists.txt \u003c/strong\u003efile for more information.\u003c/p\u003e\n\n\u003cp\u003eRelated GitHub repositories:\u003c/p\u003e\n\n\u003cul\u003e\n    \u003cli\u003e\u003ca href=\"https://github.com/endurodave/C_StateMachineWithThreads\"\u003eC Language State Machine with Threads\u003c/a\u003e - by David Lafreniere\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"https://github.com/endurodave/StateMachineWithModernDelegates\"\u003eState Machine with Modern Delegates\u003c/a\u003e - by David Lafreniere\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"https://github.com/endurodave/StateMachine\"\u003eState Machine Design in C++\u003c/a\u003e - by David Lafreniere\u003c/li\u003e\n\u003c/ul\u003e\n\n# Asynchronous Delegate Callbacks\n\n\u003cp\u003eIf you\u0026rsquo;re not familiar with a delegate, the concept is quite simple. A delegate can be thought of as a super function pointer. In C++, there\u0026#39;s no pointer type capable of pointing to all the possible function variations: instance member, virtual, const, static, and free (global). A function pointer can\u0026rsquo;t point to instance member functions, and pointers to member functions have all sorts of limitations. However, delegate classes can, in a type-safe way, point to any function provided the function signature matches. In short, a delegate points to any function with a matching signature to support anonymous function invocation.\u003c/p\u003e\n\n\u003cp\u003eAsynchronous delegates take the concept a bit further and permits anonymous invocation of any function on a client specified thread of control. The function and all arguments are safely called from a destination thread simplifying inter-thread communication and eliminating cross-threading errors.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eThe \u003ccode\u003eDelegate\u0026lt;\u0026gt;\u003c/code\u003e framework is used throughout to provide asynchronous callbacks making\u0026nbsp;an effective publisher and subscriber mechanism. A publisher exposes a delegate container interface and one or more subscribers add delegate instances to the container to receive anonymous callbacks.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eThe first place it\u0026#39;s used is within the \u003ccode\u003eSelfTest\u003c/code\u003e class where the \u003ccode\u003eSelfTest::CompletedCallback\u003c/code\u003e\u0026nbsp;delegate container allows subscribers to add delegates. Whenever a self-test completes a \u003ccode\u003eSelfTest::CompletedCallback\u003c/code\u003e callback is invoked notifying\u0026nbsp;registered clients. \u003ccode\u003eSelfTestEngine\u003c/code\u003e registers with both\u0026nbsp;\u003ccode\u003eCentrifugeTest\u003c/code\u003e and \u003ccode\u003ePressureTest\u003c/code\u003e to get asynchronously 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\u003eMulticastDelegateSafe1\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# 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 align=\"center\"\u003e\u003cimg height=\"191\" src=\"Figure_1.png\" width=\"377\" /\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\u003cstrong\u003eFigure 1: Self-Test Subsystem Inheritance Diagram\u003c/strong\u003e\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\u003eand \u003ccode\u003ePressureTest \u003c/code\u003eare members of SelfTestEngine. \u003ccode\u003eSelfTestEngine \u003c/code\u003eis 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 align=\"center\"\u003e\u003cimg height=\"370\" src=\"Figure_2.png\" width=\"530\" /\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\u003cstrong\u003eFigure 2: SelfTestEngine State Machine\u003c/strong\u003e\u003c/p\u003e\n\n\u003cp\u003eThe \u003ccode\u003eStart \u003c/code\u003eevent initiates the self-test engine.\u0026nbsp;\u0026nbsp;\u003ccode\u003eSelfTestEngine::Start()\u003c/code\u003e is an asynchronous function that reinvokes the \u003ccode\u003eStart()\u003c/code\u003e function if the caller is not on the correct execution thread. Perform a simple check whether the caller is executing on the desired thread of control. If not, a temporary asynchronous delegate is created on the stack and then invoked. The delegate and all the caller\u0026rsquo;s original function arguments are duplicated on the heap and the function is reinvoked on \u003ccode\u003em_thread\u003c/code\u003e. This is an elegant way to create asynchronous API\u0026rsquo;s with the absolute minimum of effort. 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(const StartData* data)\n{\n    // Is the caller executing on m_thread?\n    if (m_thread.GetThreadId() != WorkerThread::GetCurrentThreadId())\n    {\n        // Create an asynchronous delegate and reinvoke the function call on m_thread\n        Delegate1\u0026lt;const StartData*\u0026gt;\u0026amp; delegate = MakeDelegate(this, \u0026amp;SelfTestEngine::Start, \u0026amp;m_thread);\n        delegate(data);\n        return;\n    }\n\n    BEGIN_TRANSITION_MAP                                    // - Current State -\n        TRANSITION_MAP_ENTRY (ST_START_CENTRIFUGE_TEST)     // ST_IDLE\n        TRANSITION_MAP_ENTRY (CANNOT_HAPPEN)                // ST_COMPLETED\n        TRANSITION_MAP_ENTRY (CANNOT_HAPPEN)                // ST_FAILED\n        TRANSITION_MAP_ENTRY (EVENT_IGNORED)                // ST_START_CENTRIFUGE_TEST\n        TRANSITION_MAP_ENTRY (EVENT_IGNORED)                // ST_START_PRESSURE_TEST\n    END_TRANSITION_MAP(data)\n}\u003c/pre\u003e\n\n\u003cp\u003eWhen each self-test completes, the \u003ccode\u003eComplete \u003c/code\u003eevent fires causing the next self-test to start. After all of the tests are done, the state machine transitions to \u003ccode\u003eCompleted\u0026nbsp;\u003c/code\u003eand back to \u003ccode\u003eIdle\u003c/code\u003e. If the \u003ccode\u003eCancel \u003c/code\u003eevent 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\u003ebase 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\u003ethen adds two more states: \u003ccode\u003eStartCentrifugeTest \u003c/code\u003eand \u003ccode\u003eStartPressureTest\u003c/code\u003e.\u003c/p\u003e\n\n\u003cp\u003e\u003ccode\u003eSelfTestEngine \u003c/code\u003ehas 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\u003einstance 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 MulticastDelegateSafe1\u0026lt;const SelfTestStatus\u0026amp;\u0026gt; StatusCallback;\n\n    // Singleton instance of SelfTestEngine\n    static SelfTestEngine\u0026amp; GetInstance();\n\n    // Start the self-tests. This is a thread-safe asycnhronous function. \n    void Start(const StartData* data);\n\n    WorkerThread\u0026amp; GetThread() { return m_thread; }\n    static void InvokeStatusCallback(std::string msg);\n\nprivate:\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    StartData m_startData;\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,    StartData)\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};\u003c/pre\u003e\n\n\u003cp\u003eAs mentioned previously, the \u003ccode\u003eSelfTestEngine \u003c/code\u003eregisters for asynchronous callbacks from each sub self-tests (i.e. \u003ccode\u003eCentrifugeTest \u003c/code\u003eand \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    // Register for callbacks when sub self-test state machines complete or fail\n    m_centrifugeTest.CompletedCallback += MakeDelegate(this, \u0026amp;SelfTestEngine::Complete, \u0026amp;m_thread);\n    m_centrifugeTest.FailedCallback += MakeDelegate\u0026lt;SelfTest\u0026gt;(this, \u0026amp;SelfTest::Cancel, \u0026amp;m_thread);\n    m_pressureTest.CompletedCallback += MakeDelegate(this, \u0026amp;SelfTestEngine::Complete, \u0026amp;m_thread);\n    m_pressureTest.FailedCallback += MakeDelegate\u0026lt;SelfTest\u0026gt;(this, \u0026amp;SelfTest::Cancel, \u0026amp;m_thread);\n}\u003c/pre\u003e\n\n\u003cp\u003eThe \u003ccode\u003eSelfTest\u0026nbsp;\u003c/code\u003ebase class generates the \u003ccode\u003eCompletedCallback \u003c/code\u003eand \u003ccode\u003eFailedCallback \u003c/code\u003ewithin the \u003ccode\u003eCompleted \u003c/code\u003eand \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    if (CompletedCallback)\n        CompletedCallback();\n\n    InternalEvent(ST_IDLE);\n}\n\nSTATE_DEFINE(SelfTest, Failed, NoEventData)\n{\n    SelfTestEngine::InvokeStatusCallback(\u0026quot;SelfTest::ST_Failed\u0026quot;);\n\n    if (FailedCallback)\n        FailedCallback();\n\n    InternalEvent(ST_IDLE);\n}\u003c/pre\u003e\n\n\u003cp\u003eOne might ask why the state machines use asynchronous delegate 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\u003ecalls \u003ccode\u003eCentrifugeTest \u003c/code\u003ecalls 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\u003estate machine diagram shown below implements the centrifuge self-test described in \u0026quot;\u003ca href=\"https://www.codeproject.com/Articles/1087619/State-Machine-Design-in-Cplusplus\"\u003e\u003cstrong\u003eState Machine Design in C++\u003c/strong\u003e\u003c/a\u003e\u0026quot;. \u003ccode\u003eCentrifugeTest\u003c/code\u003e uses\u0026nbsp;state machine inheritance by inheriting the \u003ccode\u003eIdle\u003c/code\u003e, \u003ccode\u003eCompleted\u003c/code\u003e and \u003ccode\u003eFailed\u003c/code\u003e states from the \u003ccode\u003eSelfTest\u003c/code\u003e class.\u0026nbsp;The difference here is that the \u003ccode\u003eTimer\u003c/code\u003e class is used to provide \u003ccode\u003ePoll \u003c/code\u003eevents via asynchronous delegate callbacks.\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\u003cimg height=\"765\" src=\"CentrifugeTest.png\" width=\"520\" /\u003e\u003c/p\u003e\n\n\u003cp 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\u003eclass 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    /// Client\u0026#39;s register with Expired to get timer callbacks\n    SinglecastDelegate0\u0026lt;\u0026gt; Expired;\n\n    /// Constructor\n    Timer(void);\n\n    /// Destructor\n    ~Timer(void);\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    void Stop();\n...\u003c/pre\u003e\n\n\u003cp\u003eAll \u003ccode\u003eTimer \u003c/code\u003einstances 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\u003eare 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# Win32 and std::thread Worker Threads\n\n\u003cp\u003eThe source code provides two alternative \u003ccode\u003eWorkerThread \u003c/code\u003eimplementations. The Win32 version is contained within \u003cstrong\u003eWorkerThreadWin.cpp/h\u003c/strong\u003e and relies upon the Windows API. The \u003ccode\u003estd::thread\u003c/code\u003e version is located at \u003cstrong\u003eWorkerThreadStd.cpp/h\u003c/strong\u003e and uses the C++11 threading features. One of the two implementations is selected by defining either \u003ccode\u003eUSE_WIN32_THREADS \u003c/code\u003eor \u003ccode\u003eUSE_STD_THREADS \u003c/code\u003elocated within \u003cstrong\u003eDelegateOpt.h\u003c/strong\u003e.\u003c/p\u003e\n\n\u003cp\u003eSee \u003ca href=\"http://www.codeproject.com/Articles/1095196/Win-Thread-Wrapper-with-Synchronized-Start\"\u003e\u003cstrong\u003eWin32 Thread Wrapper with Synchronized Start \u003c/strong\u003e\u003c/a\u003eand \u003ca href=\"http://www.codeproject.com/Articles/1169105/Cplusplus-std-thread-Event-Loop-with-Message-Queue\"\u003e\u003cstrong\u003eC++ std::thread Event Loop with Message Queue\u0026nbsp;and Timer\u003c/strong\u003e\u003c/a\u003e for more information about the underlying thread class implementations.\u003c/p\u003e\n\n# Heap vs. Pool\n\n\u003cp\u003eOn some projects it is not desirable to utilize the heap to retrieve dynamic storage. Maybe the project is mission critical and the risk of a memory fault due to a fragmented heap in unacceptable. Or maybe heap overhead and nondeterministic execution is considered too great. Either way, the project includes a fixed block memory allocator to divert all memory allocations to a fixed block allocator. Enable the fixed block allocator on the delegate library by defining \u003ccode\u003eUSE_XALLOCATOR \u003c/code\u003ein \u003cstrong\u003eDelegateOpt.h\u003c/strong\u003e. To enable the allocator on state machines, uncomment \u003ccode\u003eXALLOCATOR \u003c/code\u003ein \u003cstrong\u003eStateMachine.h\u003c/strong\u003e.\u003c/p\u003e\n\n\u003cp\u003eSee \u003ca href=\"https://www.codeproject.com/Articles/1084801/Replace-malloc-free-with-a-Fast-Fixed-Block-Memory\"\u003e\u003cstrong\u003eReplace malloc/free with a Fast Fixed Block Memory Allocator \u003c/strong\u003e\u003c/a\u003efor more information on \u003ccode\u003exallocator\u003c/code\u003e.\u003c/p\u003e\n\n# Poll Events\n\n\u003cp\u003e\u003ccode\u003eCentrifugeTest \u003c/code\u003ehas a \u003ccode\u003eTimer\u003cstrong\u003e \u003c/strong\u003e\u003c/code\u003einstance and registers for callbacks. The callback function, a thread instance and a this 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 = MakeDelegate(this, \u0026amp;CentrifugeTest::Poll, \u0026amp;SelfTestEngine::GetInstance().GetThread());\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. Notice that when the \u003ccode\u003ePoll()\u003c/code\u003e external event function is called, a transition to either WaitForAcceleration or WaitForDeceleration\u0026nbsp;is performed based on the current state of the state machine. If \u003ccode\u003ePoll()\u003c/code\u003e is called at the wrong time, the event is silently ignored.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nvoid CentrifugeTest::Poll()\n{\n\u0026nbsp; \u0026nbsp; BEGIN_TRANSITION_MAP \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; // - Current State -\n\u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; TRANSITION_MAP_ENTRY (EVENT_IGNORED) \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; // ST_IDLE\n\u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; TRANSITION_MAP_ENTRY (EVENT_IGNORED) \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; // ST_COMPLETED\n\u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; TRANSITION_MAP_ENTRY (EVENT_IGNORED) \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; // ST_FAILED\n\u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; TRANSITION_MAP_ENTRY (EVENT_IGNORED) \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; // ST_START_TEST\n\u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; TRANSITION_MAP_ENTRY (ST_WAIT_FOR_ACCELERATION) \u0026nbsp; \u0026nbsp;// ST_ACCELERATION\n\u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; TRANSITION_MAP_ENTRY (ST_WAIT_FOR_ACCELERATION) \u0026nbsp; \u0026nbsp;// ST_WAIT_FOR_ACCELERATION\n\u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; TRANSITION_MAP_ENTRY (ST_WAIT_FOR_DECELERATION) \u0026nbsp; \u0026nbsp;// ST_DECELERATION\n\u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; TRANSITION_MAP_ENTRY (ST_WAIT_FOR_DECELERATION) \u0026nbsp; \u0026nbsp;// ST_WAIT_FOR_DECELERATION\n\u0026nbsp; \u0026nbsp; END_TRANSITION_MAP(NULL)\n}\n\nSTATE_DEFINE(CentrifugeTest, Acceleration, NoEventData)\n{\n\u0026nbsp; \u0026nbsp; SelfTestEngine::InvokeStatusCallback(\u0026quot;CentrifugeTest::ST_Acceleration\u0026quot;);\n\n\u0026nbsp; \u0026nbsp; // Start polling while waiting for centrifuge to ramp up to speed\n\u0026nbsp; \u0026nbsp; m_pollTimer.Start(10);\n}\n\u003c/pre\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)\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\u003e\nSelfTestEngine::StatusCallback += \n\u0026nbsp;     MakeDelegate(\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 += MakeDelegate(\u0026amp;SelfTestEngineStatusCallback, \u0026amp;userInterfaceThread);\n    SelfTestEngine::GetInstance().CompletedCallback += \n\u0026nbsp;        MakeDelegate(\u0026amp;SelfTestEngineCompleteCallback, \u0026amp;userInterfaceThread);\n    \n    // Start the worker threads\n    ThreadWin::StartAllThreads();\n\n    // Start self-test engine\n    StartData startData;\n    startData.shortSelfTest = TRUE;\n    SelfTestEngine::GetInstance().Start(\u0026amp;startData);\n\n    // Wait for self-test engine to complete \n    while (!selfTestEngineCompleted)\n        Sleep(10);\n\n    // Unregister for self-test engine callbacks\n    SelfTestEngine::StatusCallback -= MakeDelegate(\u0026amp;SelfTestEngineStatusCallback, \u0026amp;userInterfaceThread);\n    SelfTestEngine::GetInstance().CompletedCallback -= \n\u0026nbsp;        MakeDelegate(\u0026amp;SelfTestEngineCompleteCallback, \u0026amp;userInterfaceThread);\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\u003egenerates asynchronous callbacks on the \u003ccode\u003eUserInteface \u003c/code\u003ethread. 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)\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()\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 align=\"center\"\u003e\u003cimg height=\"385\" src=\"Figure_4.png\" width=\"628\" /\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\u003cstrong\u003eFigure 4: Console Output\u003c/strong\u003e\u003c/p\u003e\n\n# Conclusion\n\n\u003cp\u003eThe \u003ccode\u003eStateMachine \u003c/code\u003eand \u003ccode\u003eDelegate\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 delegate 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\n# References\n\n\u003cul\u003e\n\t\u003cli\u003e\u003ca href=\"http://www.codeproject.com/Articles/1087619/State-Machine-Design-in-Cplusplus\"\u003e\u003cstrong\u003eState Machine Design in C++\u003c/strong\u003e\u003c/a\u003e - by David Lafreniere\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"https://www.codeproject.com/Articles/1160934/Asynchronous-Multicast-Delegates-in-Cplusplus\"\u003e\u003cstrong\u003eAsynchronous Multicast Delegates in C++\u003c/strong\u003e\u003c/a\u003e - by David Lafreniere\u003c/li\u003e\n\t\u003cli\u003e\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 \u0026ndash; by David Lafreniere\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"http://www.codeproject.com/Articles/1092727/Asynchronous-Multicast-Callbacks-with-Inter-Thread\"\u003e\u003cstrong\u003eAsynchronous Multicast Callbacks with Inter-Thread Messaging\u003c/strong\u003e\u003c/a\u003e - by David Lafreniere\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003e\u003ca href=\"http://www.codeproject.com/Articles/1169105/Cplusplus-std-thread-Event-Loop-with-Message-Queue\"\u003eC++ std::thread Event Loop with Message Queue and Timer\u003c/a\u003e\u003c/strong\u003e - by David Lafreniere\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"https://www.codeproject.com/Articles/1095196/Win-Thread-Wrapper-with-Synchronized-Start\"\u003e\u003cstrong\u003eWin32 Thread Wrapper with Synchronized Start \u003c/strong\u003e\u003c/a\u003e- by David Lafreniere\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"http://www.codeproject.com/Articles/1084801/Replace-malloc-free-with-a-Fast-Fixed-Block-Memory\"\u003e\u003cstrong\u003eReplace malloc/free with a Fast Fixed Block Memory Allocator \u003c/strong\u003e\u003c/a\u003e- by David Lafreniere\u003c/li\u003e\n\u003c/ul\u003e\n\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2Fstatemachinewithdelegates","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fendurodave%2Fstatemachinewithdelegates","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2Fstatemachinewithdelegates/lists"}