Merge branch 'master' of ssh://git.carlh.net/home/carl/git/dcpomatic
[dcpomatic.git] / src / lib / job_manager.cc
1 /*
2     Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 /** @file  src/job_manager.cc
21  *  @brief A simple scheduler for jobs.
22  */
23
24 #include "job_manager.h"
25 #include "job.h"
26 #include "cross.h"
27 #include "analyse_audio_job.h"
28 #include "film.h"
29 #include <boost/thread.hpp>
30 #include <boost/foreach.hpp>
31 #include <iostream>
32
33 using std::string;
34 using std::list;
35 using std::cout;
36 using boost::shared_ptr;
37 using boost::weak_ptr;
38 using boost::function;
39 using boost::dynamic_pointer_cast;
40 using boost::optional;
41
42 JobManager* JobManager::_instance = 0;
43
44 JobManager::JobManager ()
45         : _terminate (false)
46         , _scheduler (0)
47 {
48
49 }
50
51 void
52 JobManager::start ()
53 {
54         _scheduler = new boost::thread (boost::bind (&JobManager::scheduler, this));
55 }
56
57 JobManager::~JobManager ()
58 {
59         {
60                 boost::mutex::scoped_lock lm (_mutex);
61                 _terminate = true;
62         }
63
64         if (_scheduler) {
65                 DCPOMATIC_ASSERT (_scheduler->joinable ());
66                 _scheduler->join ();
67         }
68
69         delete _scheduler;
70 }
71
72 shared_ptr<Job>
73 JobManager::add (shared_ptr<Job> j)
74 {
75         {
76                 boost::mutex::scoped_lock lm (_mutex);
77                 _jobs.push_back (j);
78         }
79
80         emit (boost::bind (boost::ref (JobAdded), weak_ptr<Job> (j)));
81
82         return j;
83 }
84
85 list<shared_ptr<Job> >
86 JobManager::get () const
87 {
88         boost::mutex::scoped_lock lm (_mutex);
89         return _jobs;
90 }
91
92 bool
93 JobManager::work_to_do () const
94 {
95         boost::mutex::scoped_lock lm (_mutex);
96         list<shared_ptr<Job> >::const_iterator i = _jobs.begin();
97         while (i != _jobs.end() && (*i)->finished()) {
98                 ++i;
99         }
100
101         return i != _jobs.end ();
102 }
103
104 bool
105 JobManager::errors () const
106 {
107         boost::mutex::scoped_lock lm (_mutex);
108         for (list<shared_ptr<Job> >::const_iterator i = _jobs.begin(); i != _jobs.end(); ++i) {
109                 if ((*i)->finished_in_error ()) {
110                         return true;
111                 }
112         }
113
114         return false;
115 }
116
117 void
118 JobManager::scheduler ()
119 {
120         while (true) {
121
122                 optional<string> active_job;
123
124                 {
125                         boost::mutex::scoped_lock lm (_mutex);
126                         if (_terminate) {
127                                 return;
128                         }
129
130                         BOOST_FOREACH (shared_ptr<Job> i, _jobs) {
131
132                                 if (!i->finished ()) {
133                                         active_job = i->json_name ();
134                                 }
135
136                                 if (i->running ()) {
137                                         /* Something is already happening */
138                                         break;
139                                 }
140
141                                 if (i->is_new()) {
142                                         i->start ();
143                                         /* Only start one job at once */
144                                         break;
145                                 }
146                         }
147                 }
148
149                 if (active_job != _last_active_job) {
150                         emit (boost::bind (boost::ref (ActiveJobsChanged), _last_active_job, active_job));
151                         _last_active_job = active_job;
152                 }
153
154                 dcpomatic_sleep (1);
155         }
156 }
157
158 JobManager *
159 JobManager::instance ()
160 {
161         if (_instance == 0) {
162                 _instance = new JobManager ();
163                 _instance->start ();
164         }
165
166         return _instance;
167 }
168
169 void
170 JobManager::drop ()
171 {
172         delete _instance;
173         _instance = 0;
174 }
175
176 void
177 JobManager::analyse_audio (
178         shared_ptr<const Film> film,
179         shared_ptr<const Playlist> playlist,
180         boost::signals2::connection& connection,
181         function<void()> ready
182         )
183 {
184         {
185                 boost::mutex::scoped_lock lm (_mutex);
186
187                 BOOST_FOREACH (shared_ptr<Job> i, _jobs) {
188                         shared_ptr<AnalyseAudioJob> a = dynamic_pointer_cast<AnalyseAudioJob> (i);
189                         if (a && a->playlist () == playlist) {
190                                 i->when_finished (connection, ready);
191                                 return;
192                         }
193                 }
194         }
195
196         shared_ptr<AnalyseAudioJob> job;
197
198         {
199                 boost::mutex::scoped_lock lm (_mutex);
200
201                 job.reset (new AnalyseAudioJob (film, playlist));
202                 connection = job->Finished.connect (ready);
203                 _jobs.push_back (job);
204         }
205
206         emit (boost::bind (boost::ref (JobAdded), weak_ptr<Job> (job)));
207 }