1 | // Aseprite |
---|---|
2 | // Copyright (C) 2021 Igara Studio S.A. |
3 | // Copyright (C) 2001-2018 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/job.h" |
13 | |
14 | #include "app/app.h" |
15 | #include "app/console.h" |
16 | #include "app/context.h" |
17 | #include "app/i18n/strings.h" |
18 | #include "fmt/format.h" |
19 | #include "ui/alert.h" |
20 | #include "ui/widget.h" |
21 | #include "ui/window.h" |
22 | |
23 | #include <atomic> |
24 | |
25 | static const int kMonitoringPeriod = 100; |
26 | static std::atomic<int> g_runningJobs(0); |
27 | |
28 | namespace app { |
29 | |
30 | // static |
31 | int Job::runningJobs() |
32 | { |
33 | return g_runningJobs; |
34 | } |
35 | |
36 | Job::Job(const char* jobName) |
37 | { |
38 | m_last_progress = 0.0; |
39 | m_done_flag = false; |
40 | m_canceled_flag = false; |
41 | |
42 | if (App::instance()->isGui()) { |
43 | m_alert_window = ui::Alert::create( |
44 | fmt::format(Strings::alerts_job_working(), jobName)); |
45 | m_alert_window->addProgress(); |
46 | |
47 | m_timer.reset(new ui::Timer(kMonitoringPeriod, m_alert_window.get())); |
48 | m_timer->Tick.connect(&Job::onMonitoringTick, this); |
49 | m_timer->start(); |
50 | } |
51 | } |
52 | |
53 | Job::~Job() |
54 | { |
55 | if (App::instance()->isGui()) { |
56 | ASSERT(!m_timer->isRunning()); |
57 | |
58 | if (m_alert_window) |
59 | m_alert_window->closeWindow(NULL); |
60 | } |
61 | } |
62 | |
63 | void Job::startJob() |
64 | { |
65 | m_thread = std::thread(&Job::thread_proc, this); |
66 | ++g_runningJobs; |
67 | |
68 | if (m_alert_window) { |
69 | m_alert_window->openWindowInForeground(); |
70 | |
71 | // The job was canceled by the user? |
72 | { |
73 | std::unique_lock<std::mutex> hold(m_mutex); |
74 | if (!m_done_flag) |
75 | m_canceled_flag = true; |
76 | } |
77 | |
78 | // In case of error, take the "cancel" path (i.e. it's like the |
79 | // user canceled the operation). |
80 | if (m_error) { |
81 | m_canceled_flag = true; |
82 | try { |
83 | std::rethrow_exception(m_error); |
84 | } |
85 | catch (const std::exception& ex) { |
86 | Console::showException(ex); |
87 | } |
88 | catch (...) { |
89 | Console console; |
90 | console.printf("Unknown error performing the task"); |
91 | } |
92 | } |
93 | } |
94 | } |
95 | |
96 | void Job::waitJob() |
97 | { |
98 | if (m_timer && m_timer->isRunning()) |
99 | m_timer->stop(); |
100 | |
101 | if (m_thread.joinable()) { |
102 | m_thread.join(); |
103 | |
104 | --g_runningJobs; |
105 | } |
106 | } |
107 | |
108 | void Job::jobProgress(double f) |
109 | { |
110 | m_last_progress = f; |
111 | } |
112 | |
113 | bool Job::isCanceled() |
114 | { |
115 | return m_canceled_flag; |
116 | } |
117 | |
118 | void Job::onMonitoringTick() |
119 | { |
120 | std::unique_lock<std::mutex> hold(m_mutex); |
121 | |
122 | // update progress |
123 | m_alert_window->setProgress(m_last_progress); |
124 | |
125 | // is job done? we can close the monitor |
126 | if (m_done_flag || m_canceled_flag) { |
127 | m_timer->stop(); |
128 | m_alert_window->closeWindow(NULL); |
129 | } |
130 | } |
131 | |
132 | void Job::done() |
133 | { |
134 | std::unique_lock<std::mutex> hold(m_mutex); |
135 | m_done_flag = true; |
136 | } |
137 | |
138 | // Called to start the worker thread. |
139 | void Job::thread_proc(Job* self) |
140 | { |
141 | try { |
142 | self->onJob(); |
143 | } |
144 | catch (...) { |
145 | self->m_error = std::current_exception(); |
146 | } |
147 | self->done(); |
148 | } |
149 | |
150 | } // namespace app |
151 |