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