summaryrefslogtreecommitdiff
path: root/FreeFileSync/Source/status_handler.h
blob: 469e4d0177d39b6b8ae5534131bbfd003f500751 (plain)
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
bgstack15