You have indicated via private email that your motivation for a worker thread is because your RPCs need to perform synchronous blocking operations on a database. This is a perfectly valid concern, and is one that might be shared by several other CppWAMP users.
The solution to this problem is to provide an asynchronous service wrapper around the synchronous DB service. Unfortunately, this technique is very poorly covered by the Boost documentation. The basic idea is that the service wrapper has its own private io_service and worker thread. The private io_service acts as a work queue that performs synchronous tasks sequentially on the background thread. When the service wrapper completes an operation on its background thread, it posts a user-defined handler back to the main app's io_service.
I have written up an example. I has been compiled, but not tested:
using namespace wamp;
// Represents the database API
struct Database
{
int query(const std::string& sql) {return 42;}
};
class DatabaseService
{
public:
using QueryHandler = std::function<void (int)>;
explicit DatabaseService(AsioService& iosvc)
: iosvc_(iosvc)
{}
void start()
{
// Spawn database worker thread
thread_ = std::thread([&dbIosvc_]()
{
// This keeps the private io_service running even if there's
// no pending work
AsioService::work work(workIosvc_);
// All work to be performed on the database is be posted
// to the workIosvc_ work queue. The actual DB work is executed
// in the following loop.
workIosvc_.run();
});
}
void query(std::string sql, QueryHandler handler)
{
// Post the actual work to the background io_service.
// The io_service acts like a queue, so work will be performed
// on a FIFO basis.
workIosvc_.post([this, handler]()
{
// Use the database API to obtain the result. Blocking calls
// are okay here because we are executing in the worker thread.
auto result = db_.query(sql);
// Post the asynchronous hander to the main app's io_service.
iosvc_.post(std::bind(handler, result));
});
}
private:
AsioService& iosvc_; // IO service for main app
AsioService workIosvc_; // Private IO service for database work
std::thread thread_; // Background thread for database
Database db_; // Represents database API
};
class App
{
public:
App(AsioService& iosvc, ConnectorList connectors)
: iosvc_(iosvc),
db_(iosvc),
session_(std::move(connectors))
{}
void start()
{
boost::asio::spawn(iosvc, [&](boost::asio::yield_context yield)
{
session_->connect(yield);
session_->join(Realm("myrealm"), yield);
using namespace std::placeholders;
session_->enroll(
Rpc("getUltimateAnswer"),
std::bind(&App::getUltimateAnswerRpc, this, _1, _2),
yield);
});
}
private:
Outcome getUltimateAnswerRpc(Invocation inv, std::string question)
{
// Issue a query to the asynchronous DB service. The DB service will
// post our handler to the iosvc_ when the query operation is
// complete. We then use the Invocation object to return the result
// back to the WAMP callee.
db_.query(question, [inv](int answer)
{
inv.yield({answer});
});
// Indicate to CppWAMP that the RPC result will be sent later via
// the Invocation object.
return Outcome::deferred();
}
AsioService& iosvc_;
DatabaseService db_;
CoroSession<>::Ptr session_;
};
int main()
{
AsioService iosvc;
auto tcp = connector<Json>( iosvc, TcpHost("localhost", 12345) );
App app(iosvc, {tcp});
app.start();
iosvc.run();
return 0;
}
I have not paid any attention to orderly shutdown and destruction. std::enable_shared_from_this can be invaluable for keeping objects temporarily "alive" while operations are still pending. The idiom goes something like this:
self = shared_from_this();
asyncOperation(operationArg, [this, self](HandlerArg arg)
{
// handler code
});
Because the self shared pointer is bound to the lambda passed to the io_service queue, the object issuing the asynchronous operation will be kept alive until the operation completes.
When you're troubleshooting shutdown problems, it's sometime useful to write dummy destructors that print to the console. That way you can find out if an object is being prematurely destroyed, and whether it should be kept alive using the shared_from_this technique.
The author or Boost.Asio, Christopher Kohlhoff, has seminar videos on asynchronous programming techniques. I encourage you to watch them.