A more advanced app, and what I would like to see personally, is an example and architectural discussion on design patterns involving how best to handle server requests that require more time/resources that may not be appropriate for a single-threaded server (e.g. a database server.) From a high-level perspective, my current thinking on this is:
I guess you mean any protocol that does not have an async library, or a resource-intensive task such as image processing? If there is a specific task or protocol you'd like to see, please do mention it. Even if it does not fit in the chat application architecture, we can always use it as an idea for another Servertech app.
Handle fast requests in the main single-threaded boost.asio event loop (assuming they don't access resources locked by the next bullet point.) Handle longer requests by delegating to a separate thread pool that triggers a callback when done, without blocking the single-threaded event loop. The threads in the separate thread pool do "traditional locking" via mutexes, read/write locks, etc.
Are there more modern approaches/techniques?
I think this goes the right way, but I'd try to encapsulate it in a class that exposes an async interface. So let's say your troublesome call is `get_db_customer`, which is a third party, sync function that may block for a long time. I'd go for something like: class db_client { // configure this with the number of threads you want boost::asio::thread_pool pool_; public: customer get_customer(boost::asio::yield_context yield) { // A channel is a structure to communicate between coroutines. // concurrent_channel is thread-safe boost::asio::experimental::concurrent_channel<void(error_code, customer)> chan{yield.get_executor()}; // Whatever function we pass to post() will be run in the thread pool boost::asio::post(pool_.get_executor(), [&chan] { // This is now running in the thread_pool. Call the sync function customer res = sync_get_db_customer(); // Communicate the result back to the coroutine chan.async_send(error_code(), std::move(res), boost::asio::detached); }); // The coroutine will suspend until the result is ready, allowing // other coroutines to make progress return chan.async_receive(yield); } }; This way, you don't need locking or callbacks - you can still use the approach I propose, while retaining efficiency. It's taken me some time to come up with the code and double-check it was doing the right thing. I acknowledge that teaching this to newcomers is a real need. I've raised https://github.com/anarthal/servertech-chat/issues/44 to incorporate this to the docs, and https://github.com/anarthal/servertech-chat/issues/43 to actually use this in the code, if we come up with a feature that needs it and makes sense in ServerTech code. Regards, Ruben.