2 Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
21 /** @file src/job_manager.cc
22 * @brief A simple scheduler for jobs.
25 #include "job_manager.h"
28 #include "analyse_audio_job.h"
29 #include "analyse_subtitles_job.h"
31 #include <boost/thread.hpp>
37 using std::shared_ptr;
39 using boost::function;
40 using std::dynamic_pointer_cast;
41 using boost::optional;
44 JobManager* JobManager::_instance = 0;
46 JobManager::JobManager ()
56 _scheduler = boost::thread (boost::bind(&JobManager::scheduler, this));
57 #ifdef DCPOMATIC_LINUX
58 pthread_setname_np (_scheduler.native_handle(), "job-scheduler");
62 JobManager::~JobManager ()
64 boost::this_thread::disable_interruption dis;
66 for (auto& i: _connections) {
71 boost::mutex::scoped_lock lm (_mutex);
73 _empty_condition.notify_all ();
82 JobManager::add (shared_ptr<Job> j)
85 boost::mutex::scoped_lock lm (_mutex);
87 _empty_condition.notify_all ();
90 emit (boost::bind (boost::ref (JobAdded), weak_ptr<Job> (j)));
96 JobManager::add_after (shared_ptr<Job> after, shared_ptr<Job> j)
99 boost::mutex::scoped_lock lm (_mutex);
100 list<shared_ptr<Job> >::iterator i = find (_jobs.begin(), _jobs.end(), after);
101 DCPOMATIC_ASSERT (i != _jobs.end());
103 _empty_condition.notify_all ();
106 emit (boost::bind (boost::ref (JobAdded), weak_ptr<Job> (j)));
111 list<shared_ptr<Job> >
112 JobManager::get () const
114 boost::mutex::scoped_lock lm (_mutex);
119 JobManager::work_to_do () const
121 boost::mutex::scoped_lock lm (_mutex);
122 list<shared_ptr<Job> >::const_iterator i = _jobs.begin();
123 while (i != _jobs.end() && (*i)->finished()) {
127 return i != _jobs.end ();
131 JobManager::errors () const
133 boost::mutex::scoped_lock lm (_mutex);
134 for (auto i: _jobs) {
135 if (i->finished_in_error ()) {
144 JobManager::scheduler ()
148 boost::mutex::scoped_lock lm (_mutex);
151 bool have_new = false;
152 bool have_running = false;
153 for (auto i: _jobs) {
162 if ((!have_running && have_new) || _terminate) {
166 _empty_condition.wait (lm);
173 for (auto i: _jobs) {
175 _connections.push_back (i->FinishedImmediate.connect(bind(&JobManager::job_finished, this)));
177 emit (boost::bind (boost::ref (ActiveJobsChanged), _last_active_job, i->json_name()));
178 _last_active_job = i->json_name ();
179 /* Only start one job at once */
187 JobManager::job_finished ()
190 boost::mutex::scoped_lock lm (_mutex);
191 emit (boost::bind (boost::ref (ActiveJobsChanged), _last_active_job, optional<string>()));
192 _last_active_job = optional<string>();
195 _empty_condition.notify_all ();
199 JobManager::instance ()
201 if (_instance == 0) {
202 _instance = new JobManager ();
217 JobManager::analyse_audio (
218 shared_ptr<const Film> film,
219 shared_ptr<const Playlist> playlist,
221 boost::signals2::connection& connection,
222 function<void()> ready
226 boost::mutex::scoped_lock lm (_mutex);
228 for (auto i: _jobs) {
229 shared_ptr<AnalyseAudioJob> a = dynamic_pointer_cast<AnalyseAudioJob> (i);
230 if (a && a->path() == film->audio_analysis_path(playlist) && !i->finished_cancelled()) {
231 i->when_finished (connection, ready);
237 shared_ptr<AnalyseAudioJob> job;
240 boost::mutex::scoped_lock lm (_mutex);
242 job.reset (new AnalyseAudioJob (film, playlist, from_zero));
243 connection = job->Finished.connect (ready);
244 _jobs.push_back (job);
245 _empty_condition.notify_all ();
248 emit (boost::bind (boost::ref (JobAdded), weak_ptr<Job> (job)));
253 JobManager::analyse_subtitles (
254 shared_ptr<const Film> film,
255 shared_ptr<Content> content,
256 boost::signals2::connection& connection,
257 function<void()> ready
261 boost::mutex::scoped_lock lm (_mutex);
263 for (auto i: _jobs) {
264 shared_ptr<AnalyseSubtitlesJob> a = dynamic_pointer_cast<AnalyseSubtitlesJob> (i);
265 if (a && a->path() == film->subtitle_analysis_path(content)) {
266 i->when_finished (connection, ready);
272 shared_ptr<AnalyseSubtitlesJob> job;
275 boost::mutex::scoped_lock lm (_mutex);
277 job.reset (new AnalyseSubtitlesJob(film, content));
278 connection = job->Finished.connect (ready);
279 _jobs.push_back (job);
280 _empty_condition.notify_all ();
283 emit (boost::bind (boost::ref (JobAdded), weak_ptr<Job> (job)));
288 JobManager::increase_priority (shared_ptr<Job> job)
290 bool changed = false;
293 boost::mutex::scoped_lock lm (_mutex);
294 list<shared_ptr<Job> >::iterator last = _jobs.end ();
295 for (list<shared_ptr<Job> >::iterator i = _jobs.begin(); i != _jobs.end(); ++i) {
296 if (*i == job && last != _jobs.end()) {
311 JobManager::priority_changed ()
314 boost::mutex::scoped_lock lm (_mutex);
317 for (auto i: _jobs) {
321 } else if (i->paused_by_priority ()) {
327 i->pause_by_priority ();
333 emit (boost::bind (boost::ref (JobsReordered)));
337 JobManager::decrease_priority (shared_ptr<Job> job)
339 bool changed = false;
342 boost::mutex::scoped_lock lm (_mutex);
343 for (list<shared_ptr<Job> >::iterator i = _jobs.begin(); i != _jobs.end(); ++i) {
344 list<shared_ptr<Job> >::iterator next = i;
346 if (*i == job && next != _jobs.end()) {
362 boost::mutex::scoped_lock lm (_mutex);
368 for (auto i: _jobs) {
369 if (i->pause_by_user()) {
378 JobManager::resume ()
380 boost::mutex::scoped_lock lm (_mutex);
386 _paused_job->resume ();
389 _paused_job.reset ();