#ifndef ASYNCHRONOUS_BRIDGE_H #define ASYNCHRONOUS_BRIDGE_H #include #include #include namespace sscl::cps { class AsynchronousBridge { public: AsynchronousBridge(boost::asio::io_context &io_context) : isAsyncOperationComplete(false), io_context(io_context) {} void setAsyncOperationComplete(void) { /** EXPLANATION: * This empty post()ed message is necessary to ensure that the thread * that's waiting on the io_context is signaled to wake up and check * the io_context's queue. */ isAsyncOperationComplete.store(true); boost::asio::post(io_context, []{}); } /** Blocks the calling thread in run_one() on the bridge's io_context. * Used by syncAwaitAllSettlements(); that io_context must be the caller * thread's own queue so posted completions on the caller are not starved. */ void waitForAsyncOperationCompleteOrIoContextStopped(void) { for (;;) { io_context.run_one(); if (isAsyncOperationComplete.load() || io_context.stopped()) { break; } /** EXPLANATION: * In the puppeteer and mind thread loops we call checkException() * after run() returns, but we don't have to do that here because * setException() calls stop(). * * So if an exception is set on our thread, we'll break out of this * loop due to the check for stopped() above, and that'll take us * back out to the main loop, where we'll catch the exception. */ } } bool exitedBecauseIoContextStopped(void) const { return io_context.stopped(); } private: std::atomic isAsyncOperationComplete; boost::asio::io_context &io_context; }; } // namespace sscl::cps #endif // ASYNCHRONOUS_BRIDGE_H