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;
}