#include <restinio/all.hpp>
#include <so_5/all.hpp>
#include <random>
// Message for transfer requests from RESTinio's thread to processing thread.
struct handle_request
// Message for delaying actual requests processing.
struct timeout_elapsed
std::chrono::milliseconds m_pause;
void processing_thread_func(so_5::mchain_t req_ch)
// The stuff necessary for random pause generation.
std::random_device rd;
std::mt19937 generator{rd()};
std::uniform_int_distribution<> pause_generator{350, 3500};
// The channel for delayed timeout_elapsed messages.
auto delayed_ch = so_5::create_mchain(req_ch->environment());
// This flag will be set to 'true' when some of channels will be closed.
bool stop = false;
// If some channel become closed we should set out 'stop' flag.
.on_close([&stop](const auto &) { stop = true; })
// A predicate for stopping select() function.
.stop_on([&stop]{ return stop; }),
// Read and handle handle_request messages from req_ch.
[&](handle_request cmd) {
// Generate a random pause for processing of that request.
const std::chrono::milliseconds pause{pause_generator(generator)};
// Delay processing of that request by using delayed message.
// This the pause for send_delayed.
// The further arguments are going to timeout_elapsed's
// constructor.
// Read and handle timeout_elapsed messages from delayed_ch.
[](timeout_elapsed cmd) {
// Now we can create an actual response to the request.
.set_body("Hello, World! (pause:"
+ std::to_string(cmd.m_pause.count())
+ "ms)")
int main()
// Launching SObjectizer on a separate thread.
// There is no need to start and shutdown SObjectize:
// the wrapped_env_t instance does it automatically.
so_5::wrapped_env_t sobj;
// Thread object for processing thread.
std::thread processing_thread;
// This thread should be automatically joined at exit
// (this is necessary for exception safety).
auto processing_thread_joiner = so_5::auto_join(processing_thread);
// A channel for sending requests from RESTinio's thread to
// separate processing thread.
auto req_ch = so_5::create_mchain(sobj);
// This channel should be automatically closed at scope exit
// (this is necessary for exception safety).
auto ch_closer = so_5::auto_close_drop_content(req_ch);
// Now we can start processing thread.
// If some exception will be thrown somewhere later the thread
// will be automatically stopped and joined.
processing_thread = std::thread{
processing_thread_func, req_ch
// Traits for our simple server.
struct traits_t : public restinio::default_traits_t
.request_handler([req_ch](auto req) {
// Handle only HTTP GET requests for the root.
if(restinio::http_method_get() == req->header().method() &&
"/" == req->header().path())
// Request will be delegated to the actual processing.
so_5::send<handle_request>(req_ch, req);
.cleanup_func([&] {
// Processing thread needs to be closed.
// It is better to do it manually because there can
// be requests waiting in req_ch.
return 0;