drogon icon indicating copy to clipboard operation
drogon copied to clipboard

websocket 如何支持协程

Open Andy-wg opened this issue 4 years ago • 3 comments

Andy-wg avatar Sep 15 '21 06:09 Andy-wg

Hi, WS clients and controllers does not support coroutines natively now. However, you can wrap a AsyncTask within them.

wsPtr->setMessageHandler([](const std::string &message,
                                const WebSocketClientPtr client&,
                                const WebSocketMessageType &type) {
                                [message, client, type]() -> AsyncTask {
                                    co_await you_function(...);
                                }();
}

Same works for servers

marty1885 avatar Sep 17 '21 00:09 marty1885

@marty1885 It seems that variables cannot be captured using =, Because it will be destructed, Do you know why?

int main() {
	{
		auto ptr = std::make_shared<std::string>("hh");
		drogon::async_run([ptr]() -> drogon::AsyncTask {
			std::cout << "start co_await test2()" << std::endl;
			try {
				std::cout << ptr.use_count() << std::endl;
				co_await test2();
				std::cout << ptr.use_count() << std::endl;
			} catch (...) {
				std::cout << "异常" << std::endl;
			}
			std::cout << "end co_await test2()" << std::endl;
			co_return;
		});
	}
}
➜ git:(master) ✗ ./a.out
start co_await test2()
2
test2 start sleep
test2 end sleep
0
end co_await test2()

nqf avatar Oct 26 '21 02:10 nqf

@nqf May you share the full source code and the compiler you use? I can't tell without knowing that test2 is.

My guess is that you want to use sync_wait instead of async_run. async_runc returns immediately when it needs to wait, which in your case reaches the end of the program. Thus the true execution order is:

  1. main()
  2. async_run()
  3. Your lambda coroutine
  4. std::cout << "start co_await test2()" << std::endl;
  5. std::cout << ptr.use_count() << std::endl;
  6. test2()
  7. test2 suspends the coroutine
  8. Continue executing after async_run
  9. Reached program end. Destructing everything
  10. test2 got resumed while destructing
  11. std::cout << ptr.use_count() << std::endl
  12. std::cout << "end co_await test2()" << std::endl;
  13. Exit

Or put app().run() at the end of main to keep the event loop running.

Important: sync_wait accepts a Task<T> instead of a coroutine function. The syntax is rogon::async_run([ptr]() -> drogon::Task<> {...} ());


The naming is weird. And in most cases you want to use Task insead of AsyncTask. Task<> is fully async. And AsyncTask should be named FireAndForget, it's named that way because of internal C++ details.

marty1885 avatar Oct 26 '21 04:10 marty1885