{"id":21274154,"url":"https://github.com/endurodave/asynccallback","last_synced_at":"2025-10-10T18:49:22.479Z","repository":{"id":113549470,"uuid":"96713912","full_name":"endurodave/AsyncCallback","owner":"endurodave","description":"C++ Asynchronous Multicast Callbacks","archived":false,"fork":false,"pushed_at":"2025-06-29T14:10:57.000Z","size":100,"stargazers_count":8,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-29T15:24:54.694Z","etag":null,"topics":["asynchronous-programming","callbacks","cpp","multi-threading"],"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,"zenodo":null}},"created_at":"2017-07-09T22:00:17.000Z","updated_at":"2025-06-29T14:11:00.000Z","dependencies_parsed_at":null,"dependency_job_id":"7858b86d-137d-4253-932e-1c851e00202e","html_url":"https://github.com/endurodave/AsyncCallback","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/endurodave/AsyncCallback","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FAsyncCallback","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FAsyncCallback/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FAsyncCallback/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FAsyncCallback/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/endurodave","download_url":"https://codeload.github.com/endurodave/AsyncCallback/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FAsyncCallback/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279004909,"owners_count":26083802,"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","status":"online","status_checked_at":"2025-10-10T02:00:06.843Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","callbacks","cpp","multi-threading"],"created_at":"2024-11-21T09:19:10.215Z","updated_at":"2025-10-10T18:49:22.473Z","avatar_url":"https://github.com/endurodave.png","language":"C++","readme":"![License MIT](https://img.shields.io/github/license/BehaviorTree/BehaviorTree.CPP?color=blue)\n[![conan Ubuntu](https://github.com/endurodave/AsyncCallback/actions/workflows/cmake_ubuntu.yml/badge.svg)](https://github.com/endurodave/AsyncCallback/actions/workflows/cmake_ubuntu.yml)\n[![conan Ubuntu](https://github.com/endurodave/AsyncCallback/actions/workflows/cmake_clang.yml/badge.svg)](https://github.com/endurodave/AsyncCallback/actions/workflows/cmake_clang.yml)\n[![conan Windows](https://github.com/endurodave/AsyncCallback/actions/workflows/cmake_windows.yml/badge.svg)](https://github.com/endurodave/AsyncCallback/actions/workflows/cmake_windows.yml)\n\n# Asynchronous Multicast Callbacks with Inter-Thread Messaging\n\nA C++ asynchronous callback framework simplifies passing data between threads.\n\nOriginally published on CodeProject at \u003ca href=\"http://www.codeproject.com/Articles/1092727/Asynchronous-Multicast-Callbacks-with-Inter-Thread\"\u003eAsynchronous Multicast Callbacks\u003c/a\u003e with a 4.9 out of 5.0 user rating.\n\n# Table of Contents\n\n- [Asynchronous Multicast Callbacks with Inter-Thread Messaging](#asynchronous-multicast-callbacks-with-inter-thread-messaging)\n- [Table of Contents](#table-of-contents)\n- [Getting Started](#getting-started)\n- [References](#references)\n- [Introduction](#introduction)\n- [Callbacks Background](#callbacks-background)\n- [Using the Code](#using-the-code)\n  - [SysData Example](#sysdata-example)\n  - [SysDataClient Example](#sysdataclient-example)\n  - [SysDataNoLock Example](#sysdatanolock-example)\n  - [Timer Example](#timer-example)\n- [Callback Signature Limitations](#callback-signature-limitations)\n- [Implementation](#implementation)\n- [Heap](#heap)\n- [Porting](#porting)\n- [Code Size](#code-size)\n- [Asynchronous Library Comparison](#asynchronous-library-comparison)\n- [References](#references-1)\n\n# Getting Started\n\n[CMake](https://cmake.org/) is used to create the project build files on any Windows or Linux platform. \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# References\n\n* \u003ca href=\"https://github.com/endurodave/C_AsyncCallback\"\u003eC_AsyncCallback\u003c/a\u003e - A C-language asynchronous callback library.\n* \u003ca href=\"https://github.com/endurodave/DelegateMQ\"\u003eDelegateMQ\u003c/a\u003e - Invoke any C++ callable function synchronously or  asynchronously using delegates.\n\n# Introduction\n\n\u003cp\u003eCallbacks are a powerful concept used to reduce the coupling between two pieces of code. On a multithreaded system, callbacks have limitations. What I\u0026#39;ve always wanted was a callback mechanism that crosses threads and handles all the low-level machinery to get my event data from one thread to another safety. I need a small, portable and easy to use framework. No more monster \u003ccode\u003eswitch\u003c/code\u003e statements inside a thread loop that typecast OS message queue \u003ccode\u003evoid*\u003c/code\u003e values based upon an enumeration. Create a callback. Register a callback. And the framework automagically invokes the callback with data arguments on a user specified target thread is the goal.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eThe callback solution presented here provides the following features:\u003c/p\u003e\n\n\u003col\u003e\n\t\u003cli\u003e\u003cstrong\u003eAsynchronous callbacks\u003c/strong\u003e \u0026ndash; support asynchronous callbacks to and from any thread\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eThread targeting\u003c/strong\u003e \u0026ndash; specify the destination thread for the asynchronous callback\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eCallbacks\u003c/strong\u003e \u0026ndash; invoke any C or C++ free function with a matching signature\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eType safe\u003c/strong\u003e \u0026ndash; user defined, type safe callback function data arguments\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eMember functions\u003c/strong\u003e \u0026ndash; call instance member functions\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eMulticast callbacks\u003c/strong\u003e \u0026ndash; store multiple callbacks within a list for sequential invocation\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eThread-safe\u003c/strong\u003e \u0026ndash; suitable for use on a multi-threaded system\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eCompact\u003c/strong\u003e \u0026ndash; small, easy to maintain code base consuming minimal code space\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003ePortable\u003c/strong\u003e \u0026ndash; portable to an embedded or PC-based platform\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eAny compiler\u003c/strong\u003e \u0026ndash; no advanced C++ language features\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eAny OS\u003c/strong\u003e - easy porting to any operating system\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eElegant syntax\u003c/strong\u003e \u0026ndash; intuitive and easy to use\u003c/li\u003e\n\u003c/ol\u003e\n\n\u003cp\u003eThe callback paradigm significantly eases multithreaded application development by placing the callback and callback data onto the thread of control that you specify. Exposing an asynchronous callback interface\u0026nbsp;for a single class, module or an entire subsystem is extremely easy. The framework is no more difficult to use than a standard C callback but with more features.\u003c/p\u003e\n\n# Callbacks Background\n\n\u003cp\u003eThe idea of a function callback is very useful. In callback terms, a \u003cem\u003epublisher\u003c/em\u003e defines the callback signature and allows anonymous registration of a callback function pointer. A \u003cem\u003esubscriber\u003c/em\u003e creates a function implementation conforming to the publisher\u0026#39;s callback signature and registers a callback function pointer with the publisher at runtime. The publisher code knows nothing about the subscriber code \u0026ndash; the registration and the callback invocation is anonymous.\u003c/p\u003e\n\n\u003cp\u003eNow, on a multithreaded system, you need understand synchronous vs. asynchronous callback invocations. If the callback is synchronous, the callback is executed on the caller\u0026#39;s thread of control. If you put a break point inside the callback, the stack frame will show the publisher function call and the publisher callback all synchronously invoked. There are no multithreaded issues with this scenario as everything is running on a single thread.\u003c/p\u003e\n\n\u003cp\u003eIf the publisher code has its own thread, it may invoke the callback function on its thread of control and not the subscriber\u0026#39;s thread. A publisher invoked callback can occur at any time completely independent of the subscriber\u0026rsquo;s thread of control. This cross-threading can cause problems for the subscriber if the callback code is not thread-safe since you now have another thread calling into subscriber code base at some unknown interval.\u003c/p\u003e\n\n\u003cp\u003eOne solution for making a callback function thread-safe is to post a message to the subscriber\u0026#39;s OS queue during the publisher\u0026#39;s callback. The subscriber\u0026#39;s thread later dequeues the message and calls an appropriate function. Since the callback implementation only posts a message, the callback, even if done asynchronously, is thread-safe. In this case, the asynchrony of a message queue provides the thread safety in lieu of software locks.\u003c/p\u003e\n\n\u003cp\u003eCallbacks are typically free functions, either a class \u003ccode\u003estatic\u003c/code\u003e member or a global function. In C++, instance member functions are handled differently and have significant limitations when it comes to member function pointers. I won\u0026#39;t go into all the sorted details, the topic has been covered endlessly elsewhere, but suffice to say you can\u0026#39;t have a single pointer point to all function types. This framework supports calling free functions, but offers support to get the call back onto an instance member function.\u003c/p\u003e\n\n# Using the Code\n\n\u003cp\u003eI\u0026#39;ll first present how to use the code, and then get into the implementation details.\u003c/p\u003e\n\n\u003cp\u003eA publisher uses the\u0026nbsp;\u003ccode\u003eAsycCallback\u0026lt;\u0026gt;\u003c/code\u003e class to expose a callback interface to potential subscribers. An instance is created with one template argument \u0026ndash; the user data type for function callback argument. In the example below, an \u003ccode\u003eint\u003c/code\u003e\u0026nbsp;will become the callback function argument.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nAsyncCallback\u0026lt;int\u0026gt; callback;\u003c/pre\u003e\n\n\u003cp\u003eTo subscribe to callback, create a free function (\u003ccode\u003estatic\u003c/code\u003e member or global) as shown.\u0026nbsp;I\u0026rsquo;ll explain why the \u003ccode\u003e\u0026lt;int\u0026gt;\u003c/code\u003e argument requires a \u003ccode\u003e(const int\u0026amp;, void*)\u003c/code\u003e function signature shortly.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nvoid SimpleCallback(const int\u0026amp; value, void* userData)\n{\n    cout \u0026lt;\u0026lt; \u0026quot;SimpleCallback \u0026quot; \u0026lt;\u0026lt; value \u0026lt;\u0026lt; endl;\n}\u003c/pre\u003e\n\n\u003cp\u003eThe subscriber registers to receive callbacks using the \u003ccode\u003eRegister()\u003c/code\u003e function. The first argument is a pointer to the callback function. The second argument is a pointer to a thread the callback is to be invoked on.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\ncallback.Register(\u0026amp;SimpleCallback, \u0026amp;workerThread1);\u003c/pre\u003e\n\n\u003cp\u003eWhen the publisher needs to invoke the callback for all registered subscribers, use \u003ccode\u003eoperator()\u003c/code\u003e or \u003ccode\u003eInvoke()\u003c/code\u003e. Neither function executes the callback synchronously; instead it dispatches each callback onto the destination thread of control.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\ncallback(123);\ncallback.Invoke(123);\u003c/pre\u003e\n\n\u003cp\u003eUse \u003ccode\u003eUnregister()\u003c/code\u003e to unsubscribe a callback.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\ncallback.Unregister(\u0026amp;SimpleCallback, \u0026amp;workerThread1);\u003c/pre\u003e\n\n\u003cp\u003eAlternatively, to unregister all callbacks use \u003ccode\u003eClear()\u003c/code\u003e.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\ncallback.Clear();\u003c/pre\u003e\n\n\u003cp\u003eAlways check if anyone is subscribed to the callback before invocation using one of these two methods.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nif (callback)\n    callback(123);\nif (!callback.Empty())\n    callback(123);\u003c/pre\u003e\n\n\u003cp\u003eAn \u003ccode\u003eAsyncCallback\u0026lt;\u0026gt;\u003c/code\u003e is easily used to add asynchrony to both incoming and outgoing API interfaces. The following examples show how.\u003c/p\u003e\n\n## SysData Example\n\n\u003cp\u003e\u003ccode\u003eSysData\u003c/code\u003e is a simple class showing how to expose an \u003cem\u003eoutgoing \u003c/em\u003easynchronous interface. The class stores system data and provides asynchronous subscriber\u0026nbsp;notifications when the mode changes. The class interface is shown below.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nclass SysData\n{\npublic:\n    /// Clients register with AsyncCallback to get callbacks when system mode changes\n    AsyncCallback\u0026lt;SystemModeChanged\u0026gt; SystemModeChangedCallback;\n\n    /// Get singleton instance of this class\n    static SysData\u0026amp; GetInstance();\n\n    /// Sets the system mode and notify registered clients via SystemModeChangedCallback.\n    /// @param[in] systemMode - the new system mode. \n    void SetSystemMode(SystemMode::Type systemMode);    \n\nprivate:\n    SysData();\n    ~SysData();\n\n    /// The current system mode data\n    SystemMode::Type m_systemMode;\n\n    /// Lock to make the class thread-safe\n    LOCK m_lock;\n};\u003c/pre\u003e\n\n\u003cp\u003eThe subscriber interface for\u0026nbsp;receiving\u0026nbsp;callbacks is \u003ccode\u003eSystemModeChangedCallback\u003c/code\u003e. Calling \u003ccode\u003eSetSystemMode() \u003c/code\u003esaves the new mode into \u003ccode\u003em_systemMode\u003c/code\u003e\u0026nbsp;and notifies all registered\u0026nbsp;subscribers.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nvoid SysData::SetSystemMode(SystemMode::Type systemMode)\n{\n    LockGuard lockGuard(\u0026amp;m_lock);\n\n    // Create the callback data\n    SystemModeChanged callbackData;\n    callbackData.PreviousSystemMode = m_systemMode;\n    callbackData.CurrentSystemMode = systemMode;\n\n    // Update the system mode\n    m_systemMode = systemMode;\n\n    // Callback all registered subscribers\n    if (SystemModeChangedCallback)\n        SystemModeChangedCallback(callbackData);\n}\u003c/pre\u003e\n\n## SysDataClient Example\n\n\u003cp\u003e\u003ccode\u003eSysDataClient\u003c/code\u003e is a callback subscriber and registers for notifications within the constructor. Notice the third argument to \u003ccode\u003eRegister()\u003c/code\u003e is a \u003ccode\u003ethis\u003c/code\u003e pointer. The\u0026nbsp;pointer\u0026nbsp;is passed back as \u003ccode\u003euserData \u003c/code\u003eon each callback. The framework internally does nothing with \u003ccode\u003euserData\u003c/code\u003e other that pass it back to the callback invocation. The \u003ccode\u003euserData \u003c/code\u003evalue can be anything the caller wants.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\n// Constructor\nSysDataClient() :\n    m_numberOfCallbacks(0)\n{\n    // Register for async callbacks\n    SysData::GetInstance().SystemModeChangedCallback.Register(\u0026amp;SysDataClient::CallbackFunction, \n        \u0026amp;workerThread1, this);    \n}\u003c/pre\u003e\n\n\u003cp\u003e\u003ccode\u003eSysDataClient::CallbackFunction()\u003c/code\u003e\u0026nbsp;is now called when the system mode changes. Note that the \u003ccode\u003euserData\u003c/code\u003e argument is typecast back to a \u003ccode\u003eSysDataClient\u003c/code\u003e instance. Since \u003ccode\u003eRegister()\u003c/code\u003e provided a \u003ccode\u003ethis\u003c/code\u003e pointer, the callback function is able to access any object instance or function during execution.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nstatic void CallbackFunction(const SystemModeChanged\u0026amp; data, void* userData)\n{\n    // The user data pointer originates from the 3rd argument in the Register() function\n    // Typecast the void* to SysDataClient* to access object instance data/functions.\n    SysDataClient* instance = static_cast\u0026lt;SysDataClient*\u0026gt;(userData);\n    instance-\u0026gt;m_numberOfCallbacks++;\n\n    cout \u0026lt;\u0026lt; \u0026quot;CallbackFunction \u0026quot; \u0026lt;\u0026lt; data.CurrentSystemMode \u0026lt;\u0026lt; endl;\n}\u003c/pre\u003e\n\n\u003cp\u003eWhen \u003ccode\u003eSetSystemMode()\u003c/code\u003e is called, anyone interested in the mode changes are notified asynchronously on their desired execution thread.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\n// Set new SystemMode values. Each call will invoke callbacks to all \n// registered client subscribers.\nSysData::GetInstance().SetSystemMode(SystemMode::STARTING);\nSysData::GetInstance().SetSystemMode(SystemMode::NORMAL);\u003c/pre\u003e\n\n## SysDataNoLock Example\n\n\u003cp\u003e\u003ccode\u003eSysDataNoLocks \u003c/code\u003eis an alternate implementation that uses\u0026nbsp;a \u003ccode\u003eprivate\u003c/code\u003e \u003ccode\u003eAsyncCallback\u0026lt;\u0026gt;\u003c/code\u003e\u0026nbsp;for setting the system mode asynchronously and without locks.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nclass SysDataNoLock\n{\npublic:\n\u0026nbsp;\u0026nbsp; \u0026nbsp;/// Clients register with AsyncCallback to get callbacks when system mode changes\n\u0026nbsp;\u0026nbsp; \u0026nbsp;AsyncCallback\u0026lt;SystemModeChanged\u0026gt; SystemModeChangedCallback;\n\n\u0026nbsp;\u0026nbsp; \u0026nbsp;/// Get singleton instance of this class\n\u0026nbsp;\u0026nbsp; \u0026nbsp;static SysDataNoLock\u0026amp; GetInstance();\n\n\u0026nbsp;\u0026nbsp; \u0026nbsp;/// Sets the system mode and notify registered clients via SystemModeChangedCallback.\n\u0026nbsp;\u0026nbsp; \u0026nbsp;/// @param[in] systemMode - the new system mode.\u0026nbsp;\n\u0026nbsp;\u0026nbsp; \u0026nbsp;void SetSystemMode(SystemMode::Type systemMode);\u0026nbsp;\u0026nbsp; \u0026nbsp;\n\nprivate:\n\u0026nbsp;\u0026nbsp; \u0026nbsp;SysDataNoLock();\n\u0026nbsp;\u0026nbsp; \u0026nbsp;~SysDataNoLock();\n\n\u0026nbsp;\u0026nbsp; \u0026nbsp;/// Private callback to get the SetSystemMode call onto a common thread\n\u0026nbsp;\u0026nbsp; \u0026nbsp;AsyncCallback\u0026lt;SystemMode::Type\u0026gt; SetSystemModeCallback;\n\n\u0026nbsp;\u0026nbsp; \u0026nbsp;/// Sets the system mode and notify registered clients via SystemModeChangedCallback.\n\u0026nbsp;\u0026nbsp; \u0026nbsp;/// @param[in] systemMode - the new system mode.\u0026nbsp;\n\u0026nbsp;\u0026nbsp; \u0026nbsp;/// @param[in] userData - a \u0026#39;this\u0026#39; pointer to SysDataNoLock. \u0026nbsp;\n\u0026nbsp;\u0026nbsp; \u0026nbsp;static void SetSystemModePrivate(const SystemMode::Type\u0026amp; systemMode, void* userData);\u0026nbsp;\u0026nbsp; \u0026nbsp;\n\n\u0026nbsp;\u0026nbsp; \u0026nbsp;/// The current system mode data\n\u0026nbsp;\u0026nbsp; \u0026nbsp;SystemMode::Type m_systemMode;\n};\u003c/pre\u003e\n\n\u003cp\u003eThe constructor registers\u0026nbsp;\u003ccode\u003eSetSystemModePrivate()\u003c/code\u003e\u0026nbsp;with the \u003ccode\u003eprivate\u003c/code\u003e \u003ccode\u003eSetSystemModeCallback\u003c/code\u003e.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nSysDataNoLock::SysDataNoLock() :\n    m_systemMode(SystemMode::STARTING)\n{\n    SetSystemModeCallback.Register(\u0026amp;SysDataNoLock::SetSystemModePrivate, \u0026amp;workerThread2, this);\n    workerThread2.CreateThread();\n}\u003c/pre\u003e\n\n\u003cp\u003eThe \u003ccode\u003eSetSystemMode()\u003c/code\u003e function below is an example of an asynchronous \u003cem\u003eincoming\u003c/em\u003e interface. To the caller, it looks like a normal function, but under the hood, a private member call is invoked asynchronously.\u0026nbsp;In this case, invoking \u003ccode\u003eSetSystemModeCallback\u003c/code\u003e\u0026nbsp;causes\u0026nbsp;\u003ccode\u003eSetSystemModePrivate()\u003c/code\u003e\u0026nbsp;to be called on \u003ccode\u003eworkerThread2\u003c/code\u003e.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nvoid SysDataNoLock::SetSystemMode(SystemMode::Type systemMode)\n{\n\u0026nbsp;\u0026nbsp; \u0026nbsp;// Invoke the private callback. SetSystemModePrivate() will be called on workerThread2.\n\u0026nbsp;\u0026nbsp; \u0026nbsp;SetSystemModeCallback(systemMode);\n}\u003c/pre\u003e\n\n\u003cp\u003eSince this \u003ccode\u003eprivate\u003c/code\u003e function is always invoked asynchronously on \u003ccode\u003eworkerThread2\u003c/code\u003e\u0026nbsp;it doesn\u0026#39;t require locks.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nvoid SysDataNoLock::SetSystemModePrivate(const SystemMode::Type\u0026amp; systemMode, void* userData)\n{\n\u0026nbsp;\u0026nbsp; \u0026nbsp;SysDataNoLock* instance = static_cast\u0026lt;SysDataNoLock*\u0026gt;(userData);\n\n\u0026nbsp;\u0026nbsp; \u0026nbsp;// Create the callback data\n\u0026nbsp;\u0026nbsp; \u0026nbsp;SystemModeChanged callbackData;\n\u0026nbsp;\u0026nbsp; \u0026nbsp;callbackData.PreviousSystemMode = instance-\u0026gt;m_systemMode;\n\u0026nbsp;\u0026nbsp; \u0026nbsp;callbackData.CurrentSystemMode = systemMode;\n\n\u0026nbsp;\u0026nbsp; \u0026nbsp;// Update the system mode\n\u0026nbsp;\u0026nbsp; \u0026nbsp;instance-\u0026gt;m_systemMode = systemMode;\n\n\u0026nbsp;\u0026nbsp; \u0026nbsp;// Callback all registered subscribers\n\u0026nbsp;\u0026nbsp; \u0026nbsp;if (instance-\u0026gt;SystemModeChangedCallback)\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;instance-\u0026gt;SystemModeChangedCallback(callbackData);\n}\u003c/pre\u003e\n\n## Timer Example\n\n\u003cp\u003eOnce a callback framework is in place, creating a timer callback service is trivial. Many systems need a way to generate a callback based on a timeout. Maybe it\u0026#39;s a periodic timeout for some low speed polling, or maybe an error timeout in case something doesn\u0026#39;t occur within the expected time frame. Either way, the callback must occur on a specified thread of control. An \u003ccode\u003eAsyncCallback\u0026lt;\u0026gt;\u003c/code\u003e used inside a \u003ccode\u003eTimer\u003c/code\u003e class solves this nicely.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nclass Timer\n{\npublic:\n    AsyncCallback\u0026lt;TimerData\u0026gt; Expired;\n\n    void Start(UINT32 timeout);\n    void Stop();\n    //...\n};\u003c/pre\u003e\n\n\u003cp\u003eUsers create an instance of the timer and register for the expiration. In this case, \u003ccode\u003eMyCallback()\u003c/code\u003e is called in 1000ms.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nm_timer.Expired.Register(\u0026amp;MyClass::MyCallback, \u0026amp;myThread, this);\nm_timer.Start(1000);\u003c/pre\u003e\n\n\u003cp\u003eA \u003ccode\u003eTimer \u003c/code\u003eimplementation isn\u0026#39;t offered in the examples. However, the article \u0026quot;\u003cstrong\u003e\u003ca href=\"http://www.codeproject.com/Articles/1156423/Cplusplus-State-Machine-with-Threads\"\u003eC++ State Machine with Threads\u003c/a\u003e\u003c/strong\u003e\u0026quot; contains a \u003ccode\u003eTimer\u003c/code\u003e class that shows a complete multithreaded example of \u003ccode\u003eAsyncCallback\u0026lt;\u0026gt;\u003c/code\u003e integrated with a C++ state machine.\u003c/p\u003e\n\n# Callback Signature Limitations\n\n\u003cp\u003eThis design has the following limitations imposed on all callback functions:\u003c/p\u003e\n\n\u003col\u003e\n\t\u003cli\u003eEach callback handles a single user defined argument type (\u003ccode\u003eTData\u003c/code\u003e).\u003c/li\u003e\n\t\u003cli\u003eThe two callback function arguments are always: \u003ccode\u003econst TData\u0026amp;\u003c/code\u003e\u0026nbsp;and \u003ccode\u003evoid*\u003c/code\u003e.\u003c/li\u003e\n\t\u003cli\u003eEach callback has a \u003ccode\u003evoid \u003c/code\u003ereturn type.\u003c/li\u003e\n\u003c/ol\u003e\n\n\u003cp\u003eFor instance, if an \u003ccode\u003eAsyncCallback\u0026lt;\u0026gt;\u003c/code\u003e is declared as:\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nAsyncCallback\u0026lt;MyData\u0026gt; myCallback;\u003c/pre\u003e\n\n\u003cp\u003eThe callback function signature is:\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nvoid MyCallback(const MyData\u0026amp; data, void* userData);\u003c/pre\u003e\n\n\u003cp\u003eThe design can be extended to support more than one argument if necessary. However, the design somewhat mimics what embedded programmers do all the time, which is something like:\u003c/p\u003e\n\n\u003col\u003e\n\t\u003cli\u003eDynamically create an instance to a \u003ccode\u003estruct\u003c/code\u003e or \u003ccode\u003eclass\u003c/code\u003e and populate data.\u003c/li\u003e\n\t\u003cli\u003ePost a pointer to the data through an OS message as a \u003ccode\u003evoid*\u003c/code\u003e.\u003c/li\u003e\n\t\u003cli\u003eGet the data from the OS message queue and typecast the \u003ccode\u003evoid*\u003c/code\u003e back to the original type.\u003c/li\u003e\n\t\u003cli\u003eDelete the dynamically created data.\u003c/li\u003e\n\u003c/ol\u003e\n\n\u003cp\u003eIn this design, the entire infrastructure happens automatically without any additional effort on the programmer\u0026#39;s part. If multiple data parameters are required, they must be packaged into a single \u003ccode\u003eclass\u003c/code\u003e/\u003ccode\u003estruct\u003c/code\u003e\u0026nbsp;and used as the callback data argument.\u003c/p\u003e\n\n# Implementation\n\n\u003cp\u003eThe number of lines of code for the callback framework is surprisingly low. Strip out the comments, and maybe a couple hundred lines of code that are (hopefully) easy to understand and maintain.\u003c/p\u003e\n\n\u003cp\u003e\u003ccode\u003eAsyncCallback\u0026lt;\u0026gt;\u003c/code\u003e and \u003ccode\u003eAsyncCallbackBase \u003c/code\u003eform the basis for publishing a callback interface. The classes are thread-safe. The base version is non-templatized to reduce code space. \u003ccode\u003eAsyncCallbackBase\u003c/code\u003e provides the invocation list and thread safety mechanisms.\u003c/p\u003e\n\n\u003cp\u003e\u003ccode\u003eAsyncCallback::Invoke()\u003c/code\u003e iterates over the list and dispatches callback messages to each target thread. The data is dynamically created to travel through an OS message queue.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nvoid Invoke(const TData\u0026amp; data) \n{\n    LockGuard lockGuard(GetLock());\n\n    // For each registered callback \n    InvocationNode* node = GetInvocationHead();\n    while (node != NULL)\n    {\n        // Create a new instance of callback and copy\n        const Callback* callback = new Callback(*node-\u0026gt;CallbackElement);\n\n        // Create a new instance of the callback data and copy\n        const TData* callbackData = new TData(data);\n\n        // Create a new message  instance \n        CallbackMsg* msg = new CallbackMsg(this, callback, callbackData);\n\n        // Dispatch message onto the callback destination thread. TargetInvoke()\n        // will be called by the target thread. \n        callback-\u0026gt;GetCallbackThread()-\u0026gt;DispatchCallback(msg);\n\n        // Get the next registered callback subscriber \n        node = node-\u0026gt;Next;\n    }\n}\u003c/pre\u003e\n\n\u003cp\u003e\u003ccode\u003eAsyncCallback::TargetInvoke()\u003c/code\u003e is called by target thread to actually execute the callback. Dynamic data is deleted after the callback is invoked.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nvirtual void TargetInvoke(CallbackMsg** msg) const\n{\n    const Callback* callback = (*msg)-\u0026gt;GetCallback();\n\n    // Typecast the void* back to a TData type\n    const TData* callbackData = static_cast\u0026lt;const TData*\u0026gt;((*msg)-\u0026gt;GetCallbackData());\n\n    // Typecast a generic callback function pointer to the CallbackFunc type\n    CallbackFunc func = reinterpret_cast\u0026lt;CallbackFunc\u0026gt;(callback-\u0026gt;GetCallbackFunction());\n\n    // Execute the registered callback function\n    (*func)(*callbackData, callback-\u0026gt;GetUserData());\n\n    // Delete dynamically data sent through the message queue\n    delete callbackData;\n    delete callback;\n    delete *msg;\n    *msg = NULL;\n}\u003c/pre\u003e\n\n\u003cp\u003eAsynchronous callbacks impose certain limitations because everything the callback destination thread needs must be created on the heap, packaged into a class, and placed into an OS message queue.\u003c/p\u003e\n\n\u003cp\u003eThe insertion into an OS queue is platform specific. The \u003ccode\u003eCallbackThread\u003c/code\u003e class provides the interface to be implemented on each target platform. See the \u003cstrong\u003ePorting \u003c/strong\u003esection below for a more complete discussion.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nclass CallbackThread\n{\npublic:\n    virtual void DispatchCallback(CallbackMsg* msg) = 0;\n};\u003c/pre\u003e\n\n\u003cp\u003eOnce the message is placed into the message queue, platform specific code unpacks the message and calls the \u003ccode\u003eAsyncCallbackBase::TargetInvoke()\u003c/code\u003e function and destroys dynamically allocated data.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nunsigned long WorkerThread::Process(void* parameter)\n{\n    MSG msg;\n    BOOL bRet;\n    while ((bRet = GetMessage(\u0026amp;msg, NULL, WM_USER_BEGIN, WM_USER_END)) != 0)\n    {\n        switch (msg.message)\n        {\n            case WM_DISPATCH_CALLBACK:\n            {\n                ASSERT_TRUE(msg.wParam != NULL);\n\n                // Get the ThreadMsg from the wParam value\n                ThreadMsg* threadMsg = reinterpret_cast\u0026lt;ThreadMsg*\u0026gt;(msg.wParam);\n\n                // Convert the ThreadMsg void* data back to a CallbackMsg* \n                CallbackMsg* callbackMsg = static_cast\u0026lt;CallbackMsg*\u0026gt;(threadMsg-\u0026gt;GetData()); \n\n                // Invoke the callback callback on the target thread\n                callbackMsg-\u0026gt;GetAsyncCallback()-\u0026gt;TargetInvoke(\u0026amp;callbackMsg);\n\n                // Delete dynamic data passed through message queue\n                delete threadMsg;\n                break;\n            }\n\n            case WM_EXIT_THREAD:\n                return 0;\n\n            default:\n                ASSERT();\n        }\n    }\n    return 0;\n}\u003c/pre\u003e\n\n\u003cp\u003eNotice the thread loop is unlike most systems that have a huge \u003ccode\u003eswitch\u003c/code\u003e statement handling various incoming data messages, type casting \u003ccode\u003evoid*\u003c/code\u003e data, then calling a specific function. The framework supports all callbacks with a single \u003ccode\u003eWM_DISPATCH_CALLBACK\u003c/code\u003e message. Once setup, the same small thread loop handles every callback. New publisher and subscribers come and go as the system is designed, but the code in-between doesn\u0026#39;t change.\u003c/p\u003e\n\n\u003cp\u003eThis is a huge benefit as on many systems getting data between threads takes a lot of manual steps. You constantly have to mess with each thread loop, create during sending, destroy data when receiving, and call various OS services and typecasts. Here you do none of that. All the stuff in-between is neatly handled for users.\u003c/p\u003e\n\n# Heap\n\n\u003cp\u003eThe heap is used to create dynamic data. It stems from using an invocation list and needing to send data objects through the message queue. Remember, your callback data is copied and destroyed during a callback. Most times, the callback data is POD (Plain Old Data Structure). If you have something fancier that can\u0026#39;t be bitwise copied, be sure to implement a copy constructor for the callback data.\u003c/p\u003e\n\n\u003cp\u003eOn some systems, it is undesirable to use the heap. For those situations, I use a fixed block memory allocator. The \u003ccode\u003exallocator\u003c/code\u003e implementation solves the dynamic storage issues and is much faster than the global heap. To use, just include \u003cem\u003exallocator.h\u003c/em\u003e and add the macro \u003ccode\u003eXALLOCATOR \u003c/code\u003eto the class declaration. An entire class hierarchy can use the fixed block allocator by placing \u003ccode\u003eXALLOCTOR\u003c/code\u003e in the base class.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\n#include \u0026quot;xallocator.h\u0026quot;\n\nclass Callback \n{\n    XALLOCATOR\n    // ...\n};\u003c/pre\u003e\n\n\u003cp\u003eWith \u003ccode\u003exallocator\u003c/code\u003e in place, calling \u003ccode\u003eoperator new\u003c/code\u003e or \u003ccode\u003edelete \u003c/code\u003eallows the fixed block allocator to take over the storage duties. How objects are created and destroyed is exactly the same, only the source of the memory is different. For more information on \u003ccode\u003exallocator\u003c/code\u003e, and to get the source code, see the article \u0026quot;\u003cstrong\u003e\u003ca href=\"https://github.com/endurodave/xallocator\"\u003eReplace malloc/free with a Fast Fixed Block Memory Allocator\u003c/a\u003e\u003c/strong\u003e\u0026quot;. The only files needed are \u003cem\u003eAllocator.h/cpp\u003c/em\u003e and \u003cem\u003exallocator.h/cpp\u003c/em\u003e.\u003c/p\u003e\n\n\u003cp\u003eTo use \u003ccode\u003exallocator\u003c/code\u003e in the callback framework, place \u003ccode\u003eXALLOCATOR\u003c/code\u003e\u003cstrong\u003e \u003c/strong\u003emacros in the following class definitions:\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003e\u003ccode\u003eCallback\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003eCallbackMsg\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003eInvocationNode\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eFor the platform specific files, you also include \u003ccode\u003eXALLOCATOR\u003c/code\u003e. In this example, these are:\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003e\u003ccode\u003eThreadMsg\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003eSystemModeChanged\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n# Porting\n\n\u003cp\u003eThe code is an easy port to any platform. There are only two OS services required: threads and a software lock. The code is separated into five directories.\u0026nbsp;\u003c/p\u003e\n\n\u003col\u003e\n\t\u003cli\u003e\u003ccode\u003eAsyncCallback\u003c/code\u003e\u003cstrong\u003e \u003c/strong\u003e- core framework implementation files\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003ePort\u003c/code\u003e\u003cstrong\u003e \u003c/strong\u003e\u0026ndash; port-specific files\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003eExamples\u003c/code\u003e \u0026ndash; sample code showing usage\u003c/li\u003e\n\u003c/ol\u003e\n\n\u003cp\u003eThe library has a single \u003ccode\u003eabstract\u003c/code\u003e class \u003ccode\u003eCallbackThread \u003c/code\u003ewith a single pure \u003ccode\u003evirtual\u003c/code\u003e function:\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nvirtual void DispatchCallback(CallbackMsg* msg) = 0;\u003c/pre\u003e\n\n\u003cp\u003eOn most projects, I wrap the underlying raw OS calls into a thread class to encapsulate and enforce the correct behavior. Here, I provide \u003ccode\u003eThreadWin\u003c/code\u003e as a wrapper over the \u003ccode\u003eCreateThread()\u003c/code\u003e Windows API.\u003c/p\u003e\n\n\u003cp\u003eOnce you have a thread class, just inherit the \u003ccode\u003eCallbackThread\u003c/code\u003e interface and implement the \u003ccode\u003eDispatchCallback()\u003c/code\u003e function. On Windows, a simple post to a message queue is all that is required:\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nvoid ThreadWin::DispatchCallback(CallbackMsg* msg)\n{\n    // Create a new ThreadMsg\n    ThreadMsg* threadMsg = new ThreadMsg(WM_DISPATCH_CALLBACK, msg);\n\n    // Post the message to the this thread\u0026#39;s message queue\n    PostThreadMessage(WM_DISPATCH_CALLBACK, threadMsg);\n}\u003c/pre\u003e\n\n\u003cp\u003eThe Windows thread loop gets the message and calls the \u003ccode\u003eTargetInvoke()\u003c/code\u003e function for the incoming instance. The data sent through the queue is deleted once complete.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nswitch (msg.message)\n{\n    case WM_DISPATCH_CALLBACK:\n    {\n        ASSERT_TRUE(msg.wParam != NULL);\n\n        // Get the ThreadMsg from the wParam value\n        ThreadMsg* threadMsg = reinterpret_cast\u0026lt;ThreadMsg*\u0026gt;(msg.wParam);\n\n        // Convert the ThreadMsg void* data back to a CallbackMsg* \n        CallbackMsg* callbackMsg = static_cast\u0026lt;CallbackMsg*\u0026gt;(threadMsg-\u0026gt;GetData()); \n\n        // Invoke the callback callback on the target thread\n        callbackMsg-\u0026gt;GetAsyncCallback()-\u0026gt;TargetInvoke(\u0026amp;callbackMsg);\n\n        // Delete dynamic data passed through message queue\n        delete threadMsg;\n        break;\n    }\n\n    case WM_EXIT_THREAD:\n        return 0;\n\n    default:\n        ASSERT();\n}\u003c/pre\u003e\n\n\u003cp\u003eSoftware locks are handled by the \u003ccode\u003eLockGuard\u003c/code\u003e class. This class can be updated with locks of your choice, or you can use a different mechanism. Locks are only used in a few places.\u003c/p\u003e\n\n# Code Size\n\n\u003cp\u003eTo gauge the cost of using this technique, the code was built for an ARM CPU using Keil. If deployed on a project, many \u003ccode\u003eAsyncCallback\u0026lt;\u0026gt;\u003c/code\u003e instances will be created so it needs to be space efficient.\u003c/p\u003e\n\n\u003cp\u003eThe incremental code size of for each additional \u003ccode\u003eAsyncCallback\u0026lt;\u0026gt;\u003c/code\u003e, one subscriber, one registration call, and one callback invocation is around 120 bytes using full optimization. You\u0026rsquo;d certainly use at least this much code moving data from one thread to another manually.\u003c/p\u003e\n\n# Asynchronous Library Comparison\n\n\u003cp\u003eAsynchronous function invocation allows for easy movement of data between threads. The table below summarizes the various asynchronous function invocation implementations available in C and C++.\u003c/p\u003e\n\n| Repository                                                                                            | Language | Key  Features                                                                                                                                                                                                               | Notes                                                                                                                                                                                                      |\n|-------------------------------------------------------------------------------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| \u003ca href=\"https://github.com/endurodave/DelegateMQ\"\u003eDelegateMQ\u003c/a\u003e | C++17    | * Function-like template syntax\u003cbr\u003e * Any delegate target function type (member, static, free, lambda)\u003cbr\u003e  * N target function arguments\u003cbr\u003e * N delegate subscribers\u003cbr\u003e * Variadic templates\u003cbr\u003e * Template metaprogramming      | * Modern C++\u003cbr\u003e * Invoke synchronously, asynchronously or remotely\u003cbr\u003e * Extensive unit tests\u003cbr\u003e |\n| \u003ca href=\"https://github.com/endurodave/AsyncCallback\"\u003eAsyncCallback\u003c/a\u003e                               | C++      | * Traditional template syntax\u003cbr\u003e * Delegate target function type (static, free)\u003cbr\u003e * 1 target function argument\u003cbr\u003e * N delegate subscribers                                                                                      | * Low lines of source code\u003cbr\u003e * Most compact C++ implementation\u003cbr\u003e * Any C++ compiler                                                                                                                    |\n| \u003ca href=\"https://github.com/endurodave/C_AsyncCallback\"\u003eC_AsyncCallback\u003c/a\u003e                           | C        | * Macros provide type-safety\u003cbr\u003e * Delegate target function type (static, free)\u003cbr\u003e * 1 target function argument\u003cbr\u003e * Fixed delegate subscribers (set at compile time)\u003cbr\u003e * Optional fixed block allocator                        | * Low lines of source code\u003cbr\u003e * Very compact implementation\u003cbr\u003e * Any C compiler                         \n\n# References\n\n\u003cul\u003e\n\t\u003cli\u003e\u003ca href=\"https://github.com/endurodave/xallocator\"\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\t\u003cli\u003e\u003ca href=\"https://github.com/endurodave/StdWorkerThread\"\u003e\u003cstrong\u003eC++ std::thread Event Loop with Message Queue and Timer\u003c/strong\u003e\u003c/a\u003e - by David Lafreniere\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003e\u003ca href=\"https://github.com/endurodave/StateMachineWithThreads\"\u003eC++ State Machine with Threads\u003c/a\u003e\u003c/strong\u003e - by David Lafreniere\u003c/li\u003e\n\u003c/ul\u003e\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2Fasynccallback","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fendurodave%2Fasynccallback","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2Fasynccallback/lists"}