Open timotheecour opened 7 years ago
Thank you for starting this discussion, it is very interesting.
First off, get_future().wait()
is blocking and should not be run inside an io_service task.
Secondly, the PPL Tasks are interesting, and if I remember correctly they are proposed for the C++ standard, but will not be accepted before earliest C++20. The main problem here is that using PPL Tasks will greatly affect how you write your program. You can't for instance call await
inside a regular function (that is for instance returning void
) (edit: this is not entirely correct, there are ways to synchronise async calls through blocking). This added complexity somewhat counteracts the advantages of working with an event-loop.
With respect to waiting (in a nonblocking manner) for async functions, the boost way is to use boost::asio::spawn
. See for instance: http://www.boost.org/doc/libs/1_64_0/doc/html/boost_asio/example/cpp11/spawn/echo_server.cpp. The problem here though is that you run functions, that could potentially run in parallell, sequentially. This is also an issue with PPL's await
pattern. Additionally, the stack handing is more complex.
Finally, we have our current solution, that has none of the above drawbacks, but with the drawback of potentially leading to many nested callbacks. There is no perfect solution in my opinion, but will be following the c++ standard committee's work on asio and related additions.
JavaScript's Promise is actually my favourite way of chaining async calls, but to my knowledge this is currently not possible to do with c++'s (boost::)asio. One problem I guess would be to keep the promise/future objects alive when leaving scope.
@eidheim
How about this?
boost::unique_future<int> result =
timer.async_wait(use_unique_future)
.then([](boost::unique_future<void> future){
std::cout << "calculation 1" << std::endl;
return 21;
})
.then([](boost::unique_future<int> future){
std::cout << "calculation 2" << std::endl;
return 2 * future.get();
})
;
http://www.boost.org/doc/libs/1_55_0/doc/html/thread/synchronization.html#thread.synchronization.futures.reference.unique_future.then
using namespace boost;
int main()
{
future
In http://www.boost.org/doc/libs/1_64_0/doc/html/thread/synchronization.html#thread.synchronization.futures.reference.unique_future.then you have the line (under Notes): "The returned futures behave as the ones returned from boost::async, the destructor of the future object returned from then will block. This could be subject to change in future versions.". One would have to currently keep the future object alive then, or else it will block.
I'll look into this further in the following days.
relavant to what you just said: https://stackoverflow.com/a/12527892/1426932 create the promise/future pair right in run(), and pass the promise to the thread
void callback(std::promise<void> p)
{
_job();
p.set_value();
}
In your above link, synchronous_job::run is blocking. Our case is a bit different than the one described in the link, since both the promise/future and the callback should run in the same thread, for instance in the event loop (which is the default threading strategy in Simple-Web(Socket)-Server). Additionally, the promise/future should be kept alive without returning the future (as in JavaScript where the promises are kept alive through garbage collection I guess). I think PPL Tasks solves this by always returning the tasks until eventually a wait() (or something similar) is called on the task chain.
I was a bit unclear above, what I mean by running in the same thread, is that future::wait()
should never be called, and future::get
should only be called within the future::then
callback, that is after promise::set
is called. The future::then
callback should also be run within a posted io_service task, and promise::set
should be called within an io_service task as well (but a different posted task).
edit: changed future::then
to: the future::then
callback
@eidheim
cpprest library allows
then
andwait
, eg:client.send(out_msg).wait();
(see https://stackoverflow.com/questions/34423092/websocket-library)Likewise, beast has a synchronous interface (http://vinniefalco.github.io/beast/beast/design/websocket_zaphoyd.html)
How can I call
wait
after asend
(and avoid the "callback hell" pattern (https://colintoh.com/blog/staying-sane-with-asynchronous-programming-promises-and-generators) that you suggested in https://github.com/eidheim/Simple-WebSocket-Server/blob/master/ws_examples.cpp#L79 in response to https://github.com/eidheim/Simple-WebSocket-Server/issues/24 ?EDIT: this seems very relevant: https://stackoverflow.com/questions/20709725/how-to-wait-for-an-asio-handler