08c744e0738a8faa4155d0bdf7ac083b8fcba134
[dcpomatic.git] / src / lib / job_manager.cc
1 /*
2     Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
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.
10
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.
15
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/>.
18
19 */
20
21 /** @file  src/job_manager.cc
22  *  @brief A simple scheduler for jobs.
23  */
24
25 #include "job_manager.h"
26 #include "job.h"
27 #include "cross.h"
28 #include "analyse_audio_job.h"
29 #include "film.h"
30 #include <boost/thread.hpp>
31 #include <boost/foreach.hpp>
32 #include <iostream>
33
34 using std::string;
35 using std::list;
36 using std::cout;
37 using boost::shared_ptr;
38 using boost::weak_ptr;
39 using boost::function;
40 using boost::dynamic_pointer_cast;
41 using boost::optional;
42
43 JobManager* JobManager::_instance = 0;
44
45 JobManager::JobManager ()
46         : _terminate (false)
47         , _scheduler (0)
48 {
49
50 }
51
52 void
53 JobManager::start ()
54 {
55         _scheduler = new boost::thread (boost::bind (&JobManager::scheduler, this));
56 }
57
58 JobManager::~JobManager ()
59 {
60         {
61                 boost::mutex::scoped_lock lm (_mutex);
62                 _terminate = true;
63         }
64
65         if (_scheduler) {
66                 DCPOMATIC_ASSERT (_scheduler->joinable ());
67                 _scheduler->join ();
68         }
69
70         delete _scheduler;
71 }
72
73 shared_ptr<Job>
74 JobManager::add (shared_ptr<Job> j)
75 {
76         {
77                 boost::mutex::scoped_lock lm (_mutex);
78                 _jobs.push_back (j);
79         }
80
81         emit (boost::bind (boost::ref (JobAdded), weak_ptr<Job> (j)));
82
83         return j;
84 }
85
86 list<shared_ptr<Job> >
87 JobManager::get () const
88 {
89         boost::mutex::scoped_lock lm (_mutex);
90         return _jobs;
91 }
92
93 bool
94 JobManager::work_to_do () const
95 {
96         boost::mutex::scoped_lock lm (_mutex);
97         list<shared_ptr<Job> >::const_iterator i = _jobs.begin();
98         while (i != _jobs.end() && (*i)->finished()) {
99                 ++i;
100         }
101
102         return i != _jobs.end ();
103 }
104
105 bool
106 JobManager::errors () const
107 {
108         boost::mutex::scoped_lock lm (_mutex);
109         for (list<shared_ptr<Job> >::const_iterator i = _jobs.begin(); i != _jobs.end(); ++i) {
110                 if ((*i)->finished_in_error ()) {
111                         return true;
112                 }
113         }
114
115         return false;
116 }
117
118 void
119 JobManager::scheduler ()
120 {
121         while (true) {
122
123                 optional<string> active_job;
124
125                 {
126                         boost::mutex::scoped_lock lm (_mutex);
127                         if (_terminate) {
128                                 return;
129                         }
130
131                         BOOST_FOREACH (shared_ptr<Job> i, _jobs) {
132
133                                 if (!i->finished ()) {
134                                         active_job = i->json_name ();
135                                 }
136
137                                 if (i->running ()) {
138                                         /* Something is already happening */
139                                         break;
140                                 }
141
142                                 if (i->is_new()) {
143                                         i->start ();
144                                         /* Only start one job at once */
145                                         break;
146                                 }
147                         }
148                 }
149
150                 if (active_job != _last_active_job) {
151                         emit (boost::bind (boost::ref (ActiveJobsChanged), _last_active_job, active_job));
152                         _last_active_job = active_job;
153                 }
154
155                 dcpomatic_sleep (1);
156         }
157 }
158
159 JobManager *
160 JobManager::instance ()
161 {
162         if (_instance == 0) {
163                 _instance = new JobManager ();
164                 _instance->start ();
165         }
166
167         return _instance;
168 }
169
170 void
171 JobManager::drop ()
172 {
173         delete _instance;
174         _instance = 0;
175 }
176
177 void
178 JobManager::analyse_audio (
179         shared_ptr<const Film> film,
180         shared_ptr<const Playlist> playlist,
181         boost::signals2::connection& connection,
182         function<void()> ready
183         )
184 {
185         {
186                 boost::mutex::scoped_lock lm (_mutex);
187
188                 BOOST_FOREACH (shared_ptr<Job> i, _jobs) {
189                         shared_ptr<AnalyseAudioJob> a = dynamic_pointer_cast<AnalyseAudioJob> (i);
190                         if (a && a->playlist () == playlist) {
191                                 i->when_finished (connection, ready);
192                                 return;
193                         }
194                 }
195         }
196
197         shared_ptr<AnalyseAudioJob> job;
198
199         {
200                 boost::mutex::scoped_lock lm (_mutex);
201
202                 job.reset (new AnalyseAudioJob (film, playlist));
203                 connection = job->Finished.connect (ready);
204                 _jobs.push_back (job);
205         }
206
207         emit (boost::bind (boost::ref (JobAdded), weak_ptr<Job> (job)));
208 }