{"id":21274153,"url":"https://github.com/endurodave/async-sqlite","last_synced_at":"2025-03-15T12:43:36.993Z","repository":{"id":262919461,"uuid":"888776301","full_name":"endurodave/Async-SQLite","owner":"endurodave","description":"Asynchronous SQLite using C++ Delegates","archived":false,"fork":false,"pushed_at":"2025-03-05T23:08:48.000Z","size":3213,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-06T00:19:59.231Z","etag":null,"topics":["asynchronous-programming","c","cpp","delegates","embedded-databases-wrapper","multithreading","sqlite3"],"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":"2024-11-15T01:38:17.000Z","updated_at":"2025-03-05T23:07:13.000Z","dependencies_parsed_at":null,"dependency_job_id":"70fa651e-cb2a-4f7d-afe8-e1134342385f","html_url":"https://github.com/endurodave/Async-SQLite","commit_stats":null,"previous_names":["endurodave/async-sqlite"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FAsync-SQLite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FAsync-SQLite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FAsync-SQLite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FAsync-SQLite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/endurodave","download_url":"https://codeload.github.com/endurodave/Async-SQLite/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243732252,"owners_count":20338831,"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","c","cpp","delegates","embedded-databases-wrapper","multithreading","sqlite3"],"created_at":"2024-11-21T09:19:10.192Z","updated_at":"2025-03-15T12:43:36.984Z","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/Async-SQLite/actions/workflows/cmake_ubuntu.yml/badge.svg)](https://github.com/endurodave/Async-SQLite/actions/workflows/cmake_ubuntu.yml)\n[![conan Ubuntu](https://github.com/endurodave/Async-SQLite/actions/workflows/cmake_clang.yml/badge.svg)](https://github.com/endurodave/Async-SQLite/actions/workflows/cmake_clang.yml)\n[![conan Windows](https://github.com/endurodave/Async-SQLite/actions/workflows/cmake_windows.yml/badge.svg)](https://github.com/endurodave/Async-SQLite/actions/workflows/cmake_windows.yml)\n\n# Asynchronous SQLite API using C++ Delegates\n\nAn asynchronous SQLite API wrapper implemented using a C++ delegate libraries. All target platforms are supported including Windows, Linux, and embedded systems.\n\n# Table of Contents\n\n- [Asynchronous SQLite API using C++ Delegates](#asynchronous-sqlite-api-using-c-delegates)\n- [Table of Contents](#table-of-contents)\n- [Overview](#overview)\n- [References](#references)\n- [Project Build](#project-build)\n- [Delegate Quick Start](#delegate-quick-start)\n- [Why Asynchronous SQLite](#why-asynchronous-sqlite)\n- [Asynchronous SQLite](#asynchronous-sqlite)\n- [Examples](#examples)\n  - [Simple Example](#simple-example)\n  - [Simple Example Block Execute](#simple-example-block-execute)\n  - [Multithread Example](#multithread-example)\n    - [Synchronous Blocking Execution](#synchronous-blocking-execution)\n    - [Asynchronous Non-Blocking Execution](#asynchronous-non-blocking-execution)\n    - [Test Results](#test-results)\n\n# Overview\n\nSQLite is a lightweight, serverless, self-contained SQL database engine commonly used for embedded applications and local data storage. The C++ delegate library is a multi-threaded framework capable of anonymously targeting any callable function, either synchronously or asynchronously. This delegate library is used to create an asynchronous API for SQLite. SQLite operates in a private thread of control, with all client API calls being invoked asynchronously on this private thread. The SQLite asynchronous wrapper makes no changes to the SQLite API, except for the addition of a trailing timeout argument for wait duration.\n\nThe purpose of the wrapper is twofold: First, to provide a simple asynchronous layer over SQLite, and second, to serve as a working example of an asynchronous delegate library or subsystem interface.\n\n# References\n\n* \u003ca href=\"https://github.com/endurodave/DelegateMQ\"\u003eDelegatesMQ\u003c/a\u003e - Invoke any C++ callable function synchronously, asynchronously, or on a remote endpoint.\n* \u003ca href=\"https://www.sqlite.org/\"\u003eSQLite\u003c/a\u003e - SQLite is a C-language library that implements a small, fast, self-contained, high-reliability, full-featured, SQL database engine.\n\n# Project Build\n\n\u003ca href=\"https://www.cmake.org\"\u003eCMake\u003c/a\u003e is used to create the build files. CMake is free and open-source software. Windows, Linux and other toolchains are supported. Example CMake console commands executed inside the project root directory: \n\n\u003ccode\u003ecmake -B Build -S .\u003c/code\u003e\n\n# Delegate Quick Start\n\nThe DelegateMQ contains delegates and delegate containers. The example below creates a delegate with the target function `MyTestFunc()`. The first example is a synchronously delegate function call, and the second example asynchronously. Notice the only difference is adding a thread instance `myThread` argument. See [DelegateMQ](https://github.com/endurodave/DelegateMQ) repository for more details.\n\n```cpp\n#include \"DelegateMQ.h\"\n\nusing namespace DelegateMQ;\n\nvoid MyTestFunc(int val)\n{\n    printf(\"%d\", val);\n}\n\nint main(void)\n{\n    // Create a synchronous delegate\n    auto syncDelegate = MakeDelegate(\u0026MyTestFunc);\n\n    // Invoke MyTestFunc() synchronously\n    syncDelegate(123);\n\n    // Create an asynchronous non-blocking delegate\n    auto asyncDelegate = MakeDelegate(\u0026MyTestFunc, myThread);\n\n    // Invoke MyTestFunc() asynchronously (non-blocking)\n    asyncDelegate(123);\n\n    // Create an asynchronous blocking delegate\n    auto asyncDelegateWait = MakeDelegate(\u0026MyTestFunc, myThread, WAIT_INFINITE);\n\n    // Invoke MyTestFunc() asynchronously (blocking)\n    asyncDelegateWait(123);\n}\n```\n\n# Why Asynchronous SQLite\n\n* **Improved Performance:** Offloading SQLite operations to a separate thread allows the main thread to remain responsive, enhancing overall application performance.\n* **Non-blocking Operations:** By executing database queries in a separate thread, the main application thread can continue processing other tasks without waiting for SQLite operations to complete. A callback can be used to signal completion.\n* **Atomic Operations:** Execute a series of database operations that cannot be interrupted without locks.\n* **Isolation:** Running SQLite on a private thread ensures that database-related tasks are isolated from the main application logic, reducing the risk of thread contention or deadlock in the main application.\n\nA side benefit is a real-world example of a multi-threaded application using the delegate library. \n\n# Asynchronous SQLite\n\nThe file `async_sqlite3.h` implement the asynchronous interface. Each async function matches the SQLite library except the addition of a `timeout` argument.\n\n```cpp\nnamespace async\n{\n    #undef max  // Prevent compiler error on next line if max is defined\n    constexpr auto MAX_WAIT = std::chrono::milliseconds::max();\n\n    // Call one-time at application startup\n    void sqlite3_init_async(void);\n\n    // Get a pointer to the internal thread\n    Thread* sqlite3_get_thread(void);\n\n    SQLITE_API int sqlite3_open(\n        const char* filename,   /* Database filename (UTF-8) */\n        sqlite3** ppDb,         /* OUT: SQLite db handle */\n        std::chrono::milliseconds timeout = MAX_WAIT\n    );\n\n    SQLITE_API int sqlite3_exec(\n        sqlite3* db,                /* The database on which the SQL executes */\n        const char* zSql,           /* The SQL to be executed */\n        sqlite3_callback xCallback, /* Invoke this callback routine */\n        void* pArg,                 /* First argument to xCallback() */\n        char** pzErrMsg,            /* Write error messages here */\n        std::chrono::milliseconds timeout = MAX_WAIT\n    );\n\n    SQLITE_API int sqlite3_close(\n        sqlite3* db,\n        std::chrono::milliseconds timeout = MAX_WAIT\n    );\n\n    // etc...\n}\n```\n\nThe file `async_sqlite3.cpp` implements each function. \n\n```cpp\nvoid async::sqlite3_init_async(void)\n{\n    // Create the worker thread\n    SQLiteThread.CreateThread();\n}\n\nThread* async::sqlite3_get_thread(void)\n{\n    return \u0026SQLiteThread;\n}\n\nSQLITE_API int async::sqlite3_open(\n    const char* filename,   /* Database filename (UTF-8) */\n    sqlite3** ppDb,         /* OUT: SQLite db handle */\n    std::chrono::milliseconds timeout\n)\n{\n    // Asynchronously invoke ::sqlite3_open on the SQLiteThread thread\n    auto retVal = AsyncInvoke(::sqlite3_open, timeout, filename, ppDb);\n    return retVal;\n}\n\nSQLITE_API int async::sqlite3_exec(\n    sqlite3* db,                /* The database on which the SQL executes */\n    const char* zSql,           /* The SQL to be executed */\n    sqlite3_callback xCallback, /* Invoke this callback routine */\n    void* pArg,                 /* First argument to xCallback() */\n    char** pzErrMsg,            /* Write error messages here */\n    std::chrono::milliseconds timeout\n)\n{\n    auto retVal = AsyncInvoke(::sqlite3_exec, timeout, db, zSql, xCallback, pArg, pzErrMsg);\n    return retVal;\n}\n\nSQLITE_API int async::sqlite3_close(\n    sqlite3* db,\n    std::chrono::milliseconds timeout\n)\n{\n    auto retVal = AsyncInvoke(::sqlite3_close, timeout, db);\n    return retVal;\n}\n\n// etc...\n```\n\nThe `AsyncInvoke()` helper function invokes the function asynchronously if the caller is not executing on the `SQLiteThread` thread. Otherwise, if the caller is already on the internal thread the target function is called synchronously.\n\n```cpp\n// A private worker thread instance to execute all SQLite API functions\nstatic Thread SQLiteThread(\"SQLite Thread\");\n\n/// Helper function to simplify asynchronous function calling on SQLiteThread\n/// @param[in] func - a function to invoke\n/// @param[in] timeout - the time to wait for invoke to complete\n/// @param[in] args - the function argument(s) passed to func\ntemplate \u003ctypename Func, typename Timeout, typename... Args\u003e\nauto AsyncInvoke(Func func, Timeout timeout, Args\u0026\u0026... args)\n{\n    // Deduce return type of func\n    using RetType = decltype(func(std::forward\u003cArgs\u003e(args)...));\n\n    // Is the calling function executing on the SQLiteThread thread?\n    if (SQLiteThread.GetThreadId() != Thread::GetCurrentThreadId())\n    {\n        // Create a delegate that points to func and is invoked on SQLiteThread\n        auto delegate = MakeDelegate(func, SQLiteThread, timeout);\n\n        // Invoke the delegate target function asynchronously and wait for function call to complete\n        auto retVal = delegate.AsyncInvoke(std::forward\u003cArgs\u003e(args)...);\n\n        if constexpr (std::is_void\u003cRetType\u003e::value == false)\n        {\n            // Did async function call succeed?\n            if (retVal.has_value())\n            {\n                // Return the return value to caller\n                return std::any_cast\u003cRetType\u003e(retVal.value());\n            }\n            else\n            {\n                if constexpr (std::is_same_v\u003cRetType, int\u003e)\n                    return SQLITE_ERROR;  // Special case for int\n                else\n                    return RetType();\n            }\n        }\n    }\n    else\n    {\n        // Invoke target function synchronously since we're already executing on SQLiteThread\n        if constexpr (std::is_void_v\u003cRetType\u003e)\n        {\n            func(std::forward\u003cArgs\u003e(args)...); // Synchronous call\n            return RetType(); // No return value for void\n        }\n        else\n        {\n            auto retVal = func(std::forward\u003cArgs\u003e(args)...);  // Return the result for non-void types\n            return std::any_cast\u003cRetType\u003e(retVal);\n        }\n    }\n}\n```\n\n# Examples\n\nThe `main()` function executes example code.\n\n```cpp\nint main(void)\n{\n    std::remove(\"async_mutithread_example.db\");\n    std::remove(\"async_sqlite_simple_example.db\");\n\n    // Create all worker threads\n    nonBlockingAsyncThread.CreateThread();\n    for (int i=0; i\u003cWORKER_THREAD_CNT; i++)\n        workerThreads[i].CreateThread();\n\n    // Initialize async sqlite3 interface\n    async::sqlite3_init_async();\n\n    // Optionally run unit tests\n    RunUnitTests();\n\n    // Run all examples\n    example1();\n    example2();\n    auto blockingDuration = example3();\n    auto nonBlockingDuration = example4();\n\n    // Wait for example4() to complete on nonBlockingAsyncThread\n    while (!completeFlag)\n    {\n        std::this_thread::sleep_for(std::chrono::milliseconds(10));\n    }\n\n    // Exit all worker threads\n    nonBlockingAsyncThread.ExitThread();\n    for (int i = 0; i \u003c WORKER_THREAD_CNT; i++)\n        workerThreads[i].ExitThread();\n\n    // Compare blocking and non-blocking execution times\n    std::cout \u003c\u003c \"Blocking Time: \" \u003c\u003c blockingDuration.count() \u003c\u003c \" microseconds.\" \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"Non-Blocking Time: \" \u003c\u003c nonBlockingDuration.count() \u003c\u003c \" microseconds.\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Simple Example\n\nThe `async_sqlite_simple_example()` writes to the database first and last name using the `async` API. Each function call matches the underlying SQLite library exactly. A call to `async::sqlite3_open()`, for instance, sends a function pointer and all arguments to the `SQLiteThread`, the target function is invoked on the internal thread, and the return value `rc` is returned to the caller. In short, every `async` API call injects a message into a queue for later execution on  `SQLiteThread`.\n\n```cpp\n// Simple example to create and write to the database asynchronously. \n// Use async::sqlite3_\u003cfunc\u003e series of functions within the async namespace.\nint async_sqlite_simple_example()\n{\n    sqlite3* db;\n    char* errMsg = 0;\n    int rc;\n\n    // Step 1: Open (or create) the SQLite database file\n    rc = async::sqlite3_open(\"async_sqlite_simple_example.db\", \u0026db);\n\n    if (rc) \n    {\n        fprintf(stderr, \"Can't open database: %s\\n\", sqlite3_errmsg(db));\n        return(0);\n    }\n    else \n    {\n        printf_safe(\"Opened database successfully\\n\");\n    }\n\n    // Step 2: Create a table if it does not exist\n    const char* createTableSQL = \"CREATE TABLE IF NOT EXISTS people (\"\n        \"id INTEGER PRIMARY KEY AUTOINCREMENT, \"\n        \"first_name TEXT NOT NULL, \"\n        \"last_name TEXT NOT NULL);\";\n\n    rc = async::sqlite3_exec(db, createTableSQL, callback, 0, \u0026errMsg);\n    if (rc != SQLITE_OK) \n    {\n        fprintf(stderr, \"SQL error: %s\\n\", errMsg);\n        sqlite3_free(errMsg);\n        return 1;\n    }\n    else \n    {\n        printf_safe(\"Table created successfully or already exists\\n\");\n    }\n\n    // Step 3: Insert a record\n    const char* insertSQL = \"INSERT INTO people (first_name, last_name) \"\n        \"VALUES ('John', 'Doe');\";\n\n    rc = async::sqlite3_exec(db, insertSQL, callback, 0, \u0026errMsg);\n    if (rc != SQLITE_OK) \n    {\n        fprintf(stderr, \"SQL error: %s\\n\", errMsg);\n        sqlite3_free(errMsg);\n    }\n    else \n    {\n        printf_safe(\"Record inserted successfully\\n\");\n    }\n\n    // Step 4: Verify the insertion by querying the table\n    const char* selectSQL = \"SELECT * FROM people;\";\n\n    rc = async::sqlite3_exec(db, selectSQL, callback, 0, \u0026errMsg);\n    if (rc != SQLITE_OK) \n    {\n        fprintf(stderr, \"SQL error: %s\\n\", errMsg);\n        sqlite3_free(errMsg);\n    }\n    else \n    {\n        printf_safe(\"Query executed successfully\\n\");\n    }\n\n    // Step 5: Close the database connection\n    async::sqlite3_close(db);\n    return 0;\n}\n```\n\n## Simple Example Block Execute\n\nThe previous example generated one queue message per `async` API call. Alternatively, the entire `async_sqlite_simple_example()` function can be executed on the `SQLiteThread`. An asynchronous delegate is created to invoke `async_sqlite_simple_example()` on `sqlThread`. Since all `async::sqlite3_\u003cfunc\u003e` calls are already executed on `sqlThread`, the helper function `AsyncInvoke()` will not generate a queue message when calling each database API. In this way, all the database interactions within `async_sqlite_simple_example()` are atomic and cannot be interrupted by other operations on the database.\n\n```cpp\n// Get the internal SQLite async interface thread\nThread* sqlThread = async::sqlite3_get_thread();\n\n// Create an asynchronous blocking delegate to invoke async_sqlite_simple_example()\nauto delegate = dmq::MakeDelegate(\u0026async_sqlite_simple_example, *sqlThread, async::MAX_WAIT);\n\n// Invoke async_sqlite_simple_example() on sqlThread and wait for the retVal\nauto retVal = delegate.AsyncInvoke();\n\n// If retVal has a value then the asynchronous function call succeeded\nif (retVal.has_value())\n{\n    // The async_sqlite_simple_example() return value is stored in retVal.value()\n    printf_safe(\"Return Value: %d\\n\", retVal.value());\n}\n```\n\n## Multithread Example\n\nThe `async_mutithread_example()` uses two separate threads to insert into the database concurrently. The lambda function `WriteDatabaseLambda` is executed by two threads. When both worker threads are complete, the `async_multithread_example()` function returns.\n\n```cpp\n// Async SQLite multithread example. The WriteDatabaseLambda() function is called \n// from multiple threads of control. Function returns after all threads are complete.\nint async_mutithread_example()\n{\n    char* errMsg = 0;\n    int rc;\n\n    // Step 1: Open (or create) the SQLite database file\n    rc = async::sqlite3_open(\"async_mutithread_example.db\", \u0026db_multithread);\n\n    if (rc)\n    {\n        fprintf(stderr, \"Can't open database: %s\\n\", sqlite3_errmsg(db_multithread));\n        return(0);\n    }\n    else\n    {\n        printf_safe(\"Opened database successfully\\n\");\n    }\n\n    // Step 2: Create a table if it does not exist\n    const char* createTableSQL = \"CREATE TABLE IF NOT EXISTS threads (\"\n        \"id INTEGER PRIMARY KEY AUTOINCREMENT, \"\n        \"thread_name TEXT NOT NULL, \"\n        \"cnt TEXT NOT NULL);\";\n\n    rc = async::sqlite3_exec(db_multithread, createTableSQL, callback, 0, \u0026errMsg);\n    if (rc != SQLITE_OK)\n    {\n        fprintf(stderr, \"SQL error: %s\\n\", errMsg);\n        sqlite3_free(errMsg);\n        return 1;\n    }\n    else\n    {\n        printf_safe(\"Table created successfully or already exists\\n\");\n    }\n\n    // Lambda function to write data to SQLite database\n    auto WriteDatabaseLambda = +[](std::string thread_name) -\u003e void\n    {\n        char* errMsg = 0;\n        int rc;\n        static int cnt = 0;\n\n        for (int i = 0; i \u003c 100; i++)\n        {\n            // Step 3: Insert a record\n            std::string insertSQL = \"INSERT INTO threads (thread_name, cnt) \"\n                \"VALUES ('\" + thread_name + \"', '\" + std::to_string(i) + \"');\";\n\n            rc = async::sqlite3_exec(db_multithread, insertSQL.c_str(), callback, 0, \u0026errMsg);\n            if (rc != SQLITE_OK)\n            {\n                fprintf(stderr, \"SQL error: %s\\n\", errMsg);\n                sqlite3_free(errMsg);\n            }\n            else\n            {\n                printf_safe(\"Record inserted successfully\\n\");\n            }\n        }\n\n        // Step 4: Verify the insertion by querying the table\n        const char* selectSQL = \"SELECT * FROM threads;\";\n\n        rc = async::sqlite3_exec(db_multithread, selectSQL, callback, 0, \u0026errMsg);\n        if (rc != SQLITE_OK)\n        {\n            fprintf(stderr, \"SQL error: %s\\n\", errMsg);\n            sqlite3_free(errMsg);\n        }\n        else\n        {\n            printf_safe(\"Query executed successfully\\n\");\n        }\n\n        // Last thread complete?\n        if (++cnt \u003e= WORKER_THREAD_CNT)\n        {\n            std::lock_guard\u003cstd::mutex\u003e lock(mtx);  // Lock the mutex to modify shared state\n            ready = true;  // Set the shared condition to true, meaning threads are complete\n\n            cv.notify_all();  // Notify waiting threads time to exit\n        }\n    };  // End Lambda\n\n    // Invoke WriteDatabaseLambda lambda function on worker threads\n    ready = false;\n    for (int i = 0; i \u003c WORKER_THREAD_CNT; i++)\n    {\n        // Create an async delegate to invoke WriteDatabaseLambda()\n        auto delegate = MakeDelegate(WriteDatabaseLambda, workerThreads[i]);\n        \n        // Invoke async target function WriteDatabaseLambda() on workerThread[i] non-blocking\n        // i.e. invoke the target function and don't wait for the return.\n        delegate(workerThreads[i].GetThreadName());\n    }\n\n    // Lock the mutex and wait for the signal from WriteDatabaseLambda\n    std::unique_lock\u003cstd::mutex\u003e lock(mtx);\n\n    // Wait for all WriteDatabaseLambda worker threads to complete\n    while (!ready)\n        cv.wait(lock);  // Block the thread until notified\n\n    // Step 5: Close the database connection\n    async::sqlite3_close(db_multithread);\n\n    // Invoke delegate callback indicating function is compelete\n    completeCallback(0);\n    return 0;\n}\n```\n\nThe `async_mutithread_example()` is run synchronously and asynchronously to illustrate the execution time differences from the calling thread's perspective. When using non-blocking, the calling thread is able to proceed without waiting for  lengthy database operations.\n\n```\nBlocking Time: 459135 microseconds.\nNon-Blocking Time: 17 microseconds.\n```\n\n### Synchronous Blocking Execution\n\nThe blocking `example3()` calls `async_mutithread_example()` directly. The SQLite database calls within `async_mutithread_example()` are asynchronous, however the caller must wait for all database operations to complete.\n\n```cpp\n// Run multithreaded example (blocking) and return the execution time\nstd::chrono::microseconds example3()\n{\n    auto blockingStart = std::chrono::high_resolution_clock::now();\n\n    // Call example and wait for completion\n    int retVal = async_mutithread_example();\n\n    auto blockingEnd = std::chrono::high_resolution_clock::now();\n    auto blockingDuration = std::chrono::duration_cast\u003cstd::chrono::microseconds\u003e(blockingEnd - blockingStart);\n\n    return blockingDuration;\n}\n```\n\n### Asynchronous Non-Blocking Execution \n\nThe non-blocking `example4()` invokes `async_multithread_example()` asynchronously on `nonBlockingAsyncThread` without waiting for the call to complete. A callback is used to signal completion should the caller need notification. The `callbackComplete` delegate invokes the `CompleteCallbackLambda` lambda callback just before `async_multithread_example()`  exits.\n\n```cpp\n// Run the multithreaded example (non-blocking) on nonBlockingAsyncThread thread and \n// return the execution time.\nstd::chrono::microseconds example4()\n{\n    completeFlag = false;\n\n    // Lambda function to receive the complete callback\n    auto CompleteCallbackLambda = +[](int retVal) -\u003e void\n    {\n        completeFlag = true;\n    };\n\n    // Register with delegate to receive callback\n    completeCallback += MakeDelegate(CompleteCallbackLambda);\n\n    auto nonBlockingStart = std::chrono::high_resolution_clock::now();\n\n    // Create a delegate to execute on nonBlockingAsyncThread without waiting for completion\n    auto noWaitDelegate = dmq::MakeDelegate(\u0026async_mutithread_example, nonBlockingAsyncThread);\n\n    // Call async_mutithread_example() on nonBlockingAsyncThread and don't wait for it to complete\n    noWaitDelegate.AsyncInvoke();\n\n    auto nonBlockingEnd = std::chrono::high_resolution_clock::now();\n    auto nonBlockingDuration = std::chrono::duration_cast\u003cstd::chrono::microseconds\u003e(nonBlockingEnd - nonBlockingStart);\n\n    return nonBlockingDuration;\n}\n```\n\n### Test Results\n\nUse *DB Browser for SQLite* tool to examine the results. Notice the interleaving of data by both worker threads.\n\n![Database Output](./Figure1.jpg)","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2Fasync-sqlite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fendurodave%2Fasync-sqlite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2Fasync-sqlite/lists"}