GUI2/Network Transmission: delegate connection polling to a worker thread
This commit is contained in:
parent
86f430c2dd
commit
21d21b0a26
2 changed files with 75 additions and 26 deletions
|
@ -27,36 +27,69 @@
|
|||
#include "serialization/string_utils.hpp"
|
||||
#include "wesnothd_connection.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace gui2
|
||||
{
|
||||
namespace dialogs
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
REGISTER_DIALOG(network_transmission)
|
||||
|
||||
network_transmission::pump_monitor::pump_monitor(connection_data*& connection)
|
||||
: connection_(connection)
|
||||
, window_()
|
||||
, completed_(0)
|
||||
, total_(0)
|
||||
, poller_(std::async(std::launch::async, [this]() {
|
||||
while(true) {
|
||||
// connection_ will be null if we reset the ptr in the main thread before canceling the dialog
|
||||
if(!connection_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for updates
|
||||
connection_->poll();
|
||||
|
||||
if(connection_->finished()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
completed_ = connection_->current();
|
||||
total_ = connection_->total();
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
}))
|
||||
{
|
||||
}
|
||||
|
||||
void network_transmission::pump_monitor::process(events::pump_info&)
|
||||
{
|
||||
if(!window_)
|
||||
if(!window_) {
|
||||
return;
|
||||
connection_->poll();
|
||||
if(connection_->finished()) {
|
||||
}
|
||||
|
||||
// Check if the thread is complete. If it is, loading is done.
|
||||
if(poller_.wait_for(0ms) == std::future_status::ready && poller_.get()) {
|
||||
window_.get().set_retval(retval::OK);
|
||||
} else {
|
||||
std::size_t completed, total;
|
||||
completed = connection_->current();
|
||||
total = connection_->total();
|
||||
if(total) {
|
||||
find_widget<progress_bar>(&(window_.get()), "progress", false)
|
||||
.set_percentage((completed * 100.) / total);
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << utils::si_string(completed, true, _("unit_byte^B")) << "/"
|
||||
<< utils::si_string(total, true, _("unit_byte^B"));
|
||||
if(total_) {
|
||||
find_widget<progress_bar>(&(window_.get()), "progress", false)
|
||||
.set_percentage((completed_ * 100.) / total_);
|
||||
|
||||
find_widget<label>(&(window_.get()), "numeric_progress", false)
|
||||
.set_label(ss.str());
|
||||
window_->invalidate_layout();
|
||||
}
|
||||
std::ostringstream ss;
|
||||
ss
|
||||
<< utils::si_string(completed_, true, _("unit_byte^B")) << "/"
|
||||
<< utils::si_string(total_, true, _("unit_byte^B"));
|
||||
|
||||
find_widget<label>(&(window_.get()), "numeric_progress", false)
|
||||
.set_label(ss.str());
|
||||
|
||||
window_->invalidate_layout();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,8 +109,8 @@ void network_transmission::pre_show(window& window)
|
|||
{
|
||||
// ***** ***** ***** ***** Set up the widgets ***** ***** ***** *****
|
||||
if(!subtitle_.empty()) {
|
||||
label& subtitle_label
|
||||
= find_widget<label>(&window, "subtitle", false);
|
||||
label& subtitle_label = find_widget<label>(&window, "subtitle", false);
|
||||
|
||||
subtitle_label.set_label(subtitle_);
|
||||
subtitle_label.set_use_markup(true);
|
||||
}
|
||||
|
@ -90,7 +123,18 @@ void network_transmission::post_show(window& /*window*/)
|
|||
pump_monitor_.window_.reset();
|
||||
|
||||
if(get_retval() == retval::CANCEL) {
|
||||
connection_->cancel();
|
||||
// The worker thread holds a reference to connection_ and checks whether it's null before polling.
|
||||
// The actual object to which connection_ is pointing will be destroyed by cancel(), so we're
|
||||
// setting connection_ to null here before that so the worker thread will exit next run.
|
||||
connection_data* const connection_ptr_copy = connection_;
|
||||
connection_ = nullptr;
|
||||
|
||||
// Wait for the current polling loop to conclude before exiting so we don't invalidate the pointer
|
||||
// mid-loop. The thread should return false if connection_ is null.
|
||||
pump_monitor_.poller_.wait();
|
||||
assert(pump_monitor_.poller_.get() == false);
|
||||
|
||||
connection_ptr_copy->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
|
||||
namespace gui2
|
||||
{
|
||||
namespace dialogs
|
||||
|
@ -53,15 +56,17 @@ private:
|
|||
class pump_monitor : public events::pump_monitor
|
||||
{
|
||||
public:
|
||||
connection_data*& connection_;
|
||||
virtual void process(events::pump_info&);
|
||||
virtual void process(events::pump_info&) override;
|
||||
|
||||
pump_monitor(connection_data*& connection)
|
||||
: connection_(connection), window_()
|
||||
{
|
||||
}
|
||||
pump_monitor(connection_data*& connection);
|
||||
|
||||
connection_data*& connection_;
|
||||
|
||||
boost::optional<window&> window_;
|
||||
|
||||
std::atomic_size_t completed_, total_;
|
||||
|
||||
std::future<bool> poller_;
|
||||
} pump_monitor_;
|
||||
|
||||
public:
|
||||
|
|
Loading…
Add table
Reference in a new issue