How can I create a synchronous wrapper around an async callback in C++?

Solution 1:

I created an example for you using condition variables. I don't have your framework so I used std::async to simulate an asynchronous call.

What the example basically is does start the asynchronous call, let the callback set a signal when the asynchronous function is done. The calling thread will then block until the flag is set (without spending any CPU time in the meantime).

#include <chrono>
#include <future>
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>

// use a condition variable, a mutex and a flag to build a synchronization point.
// condition variables are more like a signal between threads then a real variable!
// and are used a lot to do inter-thread signaling, because the avoid busy loops
class synchronization_point_t
{
public:
    void set()
    {
        std::unique_lock<std::mutex> lock{ m_mtx };
        m_flag = true;
        m_cv.notify_all();
    }

    void wait()
    {
        std::unique_lock<std::mutex> lock{ m_mtx };
        m_cv.wait(lock, [&] {return m_flag; });
    }

private:
    std::mutex m_mtx;
    std::condition_variable m_cv;
    bool m_flag{ false };
};

//-----------------------------------------------------------------------------

void callback(void* userdata)
{
    std::cout << "callback setting synchronization point signal\n";
    auto synchronization_point = reinterpret_cast<synchronization_point_t*>(userdata);
    synchronization_point->set();
}

void async_fn(void* userdata)
{
    std::cout << "async_fn starting\n";

    // simulate some work here
    for (std::size_t n = 0; n < 10; ++n)
    {
        std::cout << ".";
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    std::cout << "\n";

    std::cout << "async_fn calling callback\n";
    callback(userdata);
    std::cout << "async_fn done\n";
}


void fn()
{
    std::cout << "fn() creating a synchronization point\n";
    synchronization_point_t synchronization_point;

    std::cout << "fn() calling the asynchronous variant\n";
    auto future = std::async(std::launch::async, [&]
    {
        async_fn(reinterpret_cast<void*>(&synchronization_point));
    });

    // this will wait until the function has called the callbacks
    std::cout << "fn() waiting for the asynchronous call to complete\n";
    synchronization_point.wait();
    std::cout << "fn() done\n";
}

int main()
{
    std::cout << "main calling synchronous version of fn()\n";
    fn();
    std::cout << "main done\n";
    return 0;
}