Add a direct way to get asynchronous results
(It's something a bit like #7558. But unlike it, it should not depend on alerts.)
Now, to get some data from libtorrent, we have two options - either use a blocking call or receive the result asynchronously through alerts.
The first way is bad for interactive applications, because it either blocks the main thread, which can cause GUI to hang, or we have to workaround this by using some tricks with additional threads (as if making it asynchronous).
The second way is also inconvenient, as it makes the code confusing and requires additional tricks. IMO, alerts are good for use within the Observer pattern (for example, when tracking some internal events or when it makes sense to have a request and receive data in different piece of logic), but they look unsuitable when requesting data directly.
Earlier, I suggested simply implementing direct access to such data from app thread with mutex-based protection, but this was rejected due to the reluctance to mess with mutexes in libtorrent in order to ensure correct multithreaded access. I agree, it could look more complicated than the current event-based access way.
But nevertheless, I would highly like to get a friendlier way of getting the data.
Now I believe that futures could be a good option in this matter. Leaving libtorrent to its current method of implementing data access through an event queue, futures could provide the user side with a direct way to obtain the results of these access requests, deciding on its own whether to do it synchronously or asynchronously.
So all methods like torrent_handle::get_peer_info(), torrent_handle::url_seeds(), session_handle::is_listening(), session_handle::listen_port(), torrent_handle::piece_availability(), torrent_handle::get_download_queue(), torrent_handle::trackers(), torrent_handle::status() (and maybe some other similar ones not mentioned here) should return std::future instantiation with appropriate result. (This will also make previously added post_* methods and their corresponding alerts unnecessary.)
Some other methods that would make sense to use in different patterns (e.g. torrent_handle::save_resume_data()) could have both a return value of type std::future and a corresponding alert.
Honestly, I was impressed by QFuture when I made my suggestions above. I thought std::future had comparable functionality. Unfortunately std::future turned out to suck completely. But maybe Boost can still provide what I need.
I imagine the main challenge with a future-like object (or any other kind of message/notification) is that it needs to play well with some message loop. I don't think future can easily be combined with other things to wait for. e.g. a socket, a Qt event, epoll, etc.
@arvidn
Let's imagine that I need to get some data (for example, trackers) in order to do something with them. Now I can either call torrent_handle::trackers(), which leaves the logic straightforward, but it is highly undesirable to use it in a GUI thread, because it can be blocked indefinitely. Or I can use torrent_handle::post_trackers() and do the rest of the work when I receive the corresponding alert. This makes the logic more complex, especially if the same data is requested for different tasks from multiple code locations.
Using futures (provided that their implementation supports continuations), I could have straightforward logic, but not use blocking calls (unless absolutely necessary). For example:
t.get_trackers().then([]
{
// do something
});
... or:
t.get_resume_data().then([]
{
// export torrent file
});
I imagine the main challenge with a future-like object (or any other kind of message/notification) is that it needs to play well with some message loop. I don't think future can easily be combined with other things to wait for. e.g. a socket, a Qt event, epoll, etc.
libtorrent already uses Boost.Asio as an abstraction for waiting on things, and Asio has its own message loop already. Can't libtorrent use from it the use_future_t and use_promise_t types? (It also has experimental::promise)
@glassez what do you think?
Can't libtorrent use from it the use_future_t and use_promise_t types? (It also has experimental::promise)
@glassez what do you think?
As I said above, I'm disappointed in std::future. It doesn't allow you to add continuations (which is exactly what I'm interested in), unlike its more advanced counterparts like boost::future and QFuture. There is also std::experimental::future. But it's also very sad, since it's still "experimental" since c++11 (so I doubt it's available for general use).
For me, the most appropriate way (workaround) turned out to be the implementation of futures (QFuture namely) support at the application level, by calling blocking accessors in separate thread. Unfortunately, not all data can be obtained in this way (for example, the "resume data" has only an asynchronous method of receiving through an alert) and it looks like adding a synchronous/blocking method (#7869) is stuck.
Yeah but as @arvidn said, it's not enough to have a future object by itself, however good its interface may be, you also need it to be using a message loop that is already used by the things you care about. For libtorrent that would be sockets, epoll, etc. as its concurrency has to do with networking. Now libtorrent happens to make heavy use of Boost.Asio to abstract a lot of this stuff already, which has its own message loop, and as I said Asio has the use_future_t and use_promise_t types (and also experimental::promise) So can't you make use of these future-like objects @glassez ?