1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
// *****************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
// *****************************************************************************
#ifndef STATUS_HANDLER_H_81704805908341534
#define STATUS_HANDLER_H_81704805908341534
#include <thread>
#include <functional>
//#include <zen/error_log.h>
#include "base/process_callback.h"
#include "return_codes.h"
namespace fff
{
bool uiUpdateDue(bool force = false); //test if a specific amount of time is over
/* Updating GUI is fast! time per call to ProcessCallback::forceUiRefresh()
- Comparison 0.025 ms
- Synchronization 0.74 ms (despite complex graph control!) */
//Exception class used to abort the "compare" and "sync" process
class CancelProcess {};
enum class CancelReason
{
user,
firstError,
};
//GUI may want to abort process
struct CancelCallback
{
virtual ~CancelCallback() {}
virtual void userRequestCancel() = 0;
};
struct ProgressStats
{
int items = 0;
int64_t bytes = 0;
bool operator==(const ProgressStats&) const = default;
};
//common statistics "everybody" needs
struct Statistics
{
virtual ~Statistics() {}
virtual ProcessPhase currentPhase() const = 0;
virtual ProgressStats getCurrentStats() const = 0;
virtual ProgressStats getTotalStats () const = 0;
struct ErrorStats
{
int errorCount;
int warningCount;
};
virtual ErrorStats getErrorStats() const = 0;
virtual std::optional<CancelReason> taskCancelled() const = 0;
virtual const std::wstring& currentStatusText() const = 0;
};
struct ProcessSummary
{
std::chrono::system_clock::time_point startTime;
TaskResult result = TaskResult::cancelled;
std::vector<std::wstring> jobNames; //may be empty
ProgressStats statsProcessed;
ProgressStats statsTotal;
std::chrono::milliseconds totalTime{};
};
//partial callback implementation with common functionality for "batch", "GUI/Compare" and "GUI/Sync"
class StatusHandler : public ProcessCallback, public CancelCallback, public Statistics
{
public:
//StatusHandler() {}
//implement parts of ProcessCallback
void initNewPhase(int itemsTotal, int64_t bytesTotal, ProcessPhase phase) override //(throw X)
{
assert((itemsTotal < 0) == (bytesTotal < 0));
currentPhase_ = phase;
statsCurrent_ = {};
statsTotal_ = {itemsTotal, bytesTotal};
}
void updateDataProcessed(int itemsDelta, int64_t bytesDelta) override { updateData(statsCurrent_, itemsDelta, bytesDelta); } //note: these methods MUST NOT throw in order
void updateDataTotal (int itemsDelta, int64_t bytesDelta) override { updateData(statsTotal_, itemsDelta, bytesDelta); } //to allow usage within destructors!
void requestUiUpdate(bool force) final //throw CancelProcess
{
if (uiUpdateDue(force))
{
const bool abortRequestedBefore = static_cast<bool>(cancelRequested_);
forceUiUpdateNoThrow();
//triggered by userRequestCancel()
// => sufficient to evaluate occasionally when uiUpdateDue()!
// => refresh *before* throwing: support requestUiUpdate() during destruction
if (cancelRequested_)
{
if (!abortRequestedBefore)
forceUiUpdateNoThrow(); //immediately show the "Stop requested..." status after user clicked cancel
throw CancelProcess();
}
}
}
virtual void forceUiUpdateNoThrow() = 0; //noexcept
void updateStatus(std::wstring&& msg) final //throw CancelProcess
{
//assert(!msg.empty()); -> possible, e.g. start of parallel scan
statusText_ = std::move(msg); //update *before* running operations that can throw
requestUiUpdate(false /*force*/); //throw CancelProcess
}
[[noreturn]] void cancelProcessNow(CancelReason reason)
{
if (!cancelRequested_ || reason == CancelReason::user) //CancelReason::user overwrites CancelReason::firstError
cancelRequested_ = reason;
forceUiUpdateNoThrow(); //flush GUI to show new cancelled state
throw CancelProcess();
}
//implement CancelCallback
void userRequestCancel() final
{
cancelRequested_ = CancelReason::user; //may overwrite CancelReason::firstError
} //called from GUI code: this does NOT call cancelProcessNow() immediately, but later when we're out of the C GUI call stack
//=> don't call forceUiUpdateNoThrow() here!
//implement Statistics
ProcessPhase currentPhase() const final { return currentPhase_; }
ProgressStats getCurrentStats() const override { return statsCurrent_; }
ProgressStats getTotalStats () const override { return statsTotal_; }
const std::wstring& currentStatusText() const override { return statusText_; }
std::optional<CancelReason> taskCancelled() const override { return cancelRequested_; }
private:
void updateData(ProgressStats& stats, int itemsDelta, int64_t bytesDelta)
{
assert(stats.items >= 0);
assert(stats.bytes >= 0);
stats.items += itemsDelta;
stats.bytes += bytesDelta;
}
ProcessPhase currentPhase_ = ProcessPhase::none;
ProgressStats statsCurrent_;
ProgressStats statsTotal_ {-1, -1};
std::wstring statusText_;
std::optional<CancelReason> cancelRequested_;
};
void delayAndCountDown(std::chrono::steady_clock::time_point delayUntil, const std::function<void(const std::wstring& timeRemMsg)>& notifyStatus);
}
#endif //STATUS_HANDLER_H_81704805908341534
|