Do a topological sort of the route list before passing it to
[ardour.git] / libs / ardour / butler.cc
1 /*
2     Copyright (C) 1999-2009 Paul Davis
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 #include <errno.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <poll.h>
24 #include "pbd/error.h"
25 #include "pbd/pthread_utils.h"
26 #include "ardour/butler.h"
27 #include "ardour/crossfade.h"
28 #include "ardour/io.h"
29 #include "ardour/midi_diskstream.h"
30 #include "ardour/session.h"
31 #include "ardour/track.h"
32 #include "ardour/auditioner.h"
33
34 #include "i18n.h"
35
36 using namespace PBD;
37
38 namespace ARDOUR {
39
40 Butler::Butler(Session& s)
41         : SessionHandleRef (s)
42         , thread(0)
43         , audio_dstream_capture_buffer_size(0)
44         , audio_dstream_playback_buffer_size(0)
45         , midi_dstream_buffer_size(0)
46         , pool_trash(16)
47 {
48         g_atomic_int_set(&should_do_transport_work, 0);
49         SessionEvent::pool->set_trash (&pool_trash);
50
51         Config->ParameterChanged.connect_same_thread (*this, boost::bind (&Butler::config_changed, this, _1));
52 }
53
54 Butler::~Butler()
55 {
56         terminate_thread ();
57 }
58
59 void
60 Butler::config_changed (std::string p)
61 {
62         if (p == "playback-buffer-seconds") {
63                 /* size is in Samples, not bytes */
64                 audio_dstream_playback_buffer_size = (uint32_t) floor (Config->get_audio_playback_buffer_seconds() * _session.frame_rate());
65                 _session.adjust_playback_buffering ();
66         } else if (p == "capture-buffer-seconds") {
67                 audio_dstream_capture_buffer_size = (uint32_t) floor (Config->get_audio_capture_buffer_seconds() * _session.frame_rate());
68                 _session.adjust_capture_buffering ();
69         }
70 }
71
72 int
73 Butler::start_thread()
74 {
75         const float rate = (float)_session.frame_rate();
76
77         /* size is in Samples, not bytes */
78         audio_dstream_capture_buffer_size = (uint32_t) floor (Config->get_audio_capture_buffer_seconds() * rate);
79         audio_dstream_playback_buffer_size = (uint32_t) floor (Config->get_audio_playback_buffer_seconds() * rate);
80
81         /* size is in bytes
82          * XXX: Jack needs to tell us the MIDI buffer size
83          * (i.e. how many MIDI bytes we might see in a cycle)
84          */
85         midi_dstream_buffer_size = (uint32_t) floor (Config->get_midi_track_buffer_seconds() * rate);
86
87         MidiDiskstream::set_readahead_frames ((framecnt_t) (Config->get_midi_readahead() * rate));
88
89         Crossfade::set_buffer_size (audio_dstream_playback_buffer_size);
90
91         should_run = false;
92
93         if (pipe (request_pipe)) {
94                 error << string_compose(_("Cannot create transport request signal pipe (%1)"),
95                                 strerror (errno)) << endmsg;
96                 return -1;
97         }
98
99         if (fcntl (request_pipe[0], F_SETFL, O_NONBLOCK)) {
100                 error << string_compose(_("UI: cannot set O_NONBLOCK on butler request pipe (%1)"),
101                                 strerror (errno)) << endmsg;
102                 return -1;
103         }
104
105         if (fcntl (request_pipe[1], F_SETFL, O_NONBLOCK)) {
106                 error << string_compose(_("UI: cannot set O_NONBLOCK on butler request pipe (%1)"),
107                                 strerror (errno)) << endmsg;
108                 return -1;
109         }
110
111         if (pthread_create_and_store ("disk butler", &thread, _thread_work, this)) {
112                 error << _("Session: could not create butler thread") << endmsg;
113                 return -1;
114         }
115
116         //pthread_detach (thread);
117
118         return 0;
119 }
120
121 void
122 Butler::terminate_thread ()
123 {
124         if (thread) {
125                 void* status;
126                 const char c = Request::Quit;
127                 (void) ::write (request_pipe[1], &c, 1);
128                 pthread_join (thread, &status);
129         }
130 }
131
132 void *
133 Butler::_thread_work (void* arg)
134 {
135         SessionEvent::create_per_thread_pool ("butler events", 4096);
136         pthread_set_name (X_("butler"));
137         return ((Butler *) arg)->thread_work ();
138 }
139
140 void *
141 Butler::thread_work ()
142 {
143         uint32_t err = 0;
144
145         struct pollfd pfd[1];
146         bool disk_work_outstanding = false;
147         RouteList::iterator i;
148
149         while (true) {
150                 pfd[0].fd = request_pipe[0];
151                 pfd[0].events = POLLIN|POLLERR|POLLHUP;
152
153                 if (poll (pfd, 1, (disk_work_outstanding ? 0 : -1)) < 0) {
154
155                         if (errno == EINTR) {
156                                 continue;
157                         }
158
159                         error << string_compose (_("poll on butler request pipe failed (%1)"),
160                                           strerror (errno))
161                               << endmsg;
162                         break;
163                 }
164
165                 if (pfd[0].revents & ~POLLIN) {
166                         error << string_compose (_("Error on butler thread request pipe: fd=%1 err=%2"), pfd[0].fd, pfd[0].revents) << endmsg;
167                         break;
168                 }
169
170                 if (pfd[0].revents & POLLIN) {
171
172                         char req;
173
174                         /* empty the pipe of all current requests */
175
176                         while (1) {
177                                 size_t nread = ::read (request_pipe[0], &req, sizeof (req));
178                                 if (nread == 1) {
179
180                                         switch ((Request::Type) req) {
181
182                                         case Request::Wake:
183                                                 break;
184
185                                         case Request::Run:
186                                                 should_run = true;
187                                                 break;
188
189                                         case Request::Pause:
190                                                 should_run = false;
191                                                 break;
192
193                                         case Request::Quit:
194                                                 pthread_exit_pbd (0);
195                                                 /*NOTREACHED*/
196                                                 break;
197
198                                         default:
199                                                 break;
200                                         }
201
202                                 } else if (nread == 0) {
203                                         break;
204                                 } else if (errno == EAGAIN) {
205                                         break;
206                                 } else {
207                                         fatal << _("Error reading from butler request pipe") << endmsg;
208                                         /*NOTREACHED*/
209                                 }
210                         }
211                 }
212
213
214 restart:
215                 disk_work_outstanding = false;
216
217                 if (transport_work_requested()) {
218                         _session.butler_transport_work ();
219                 }
220
221                 boost::shared_ptr<RouteList> rl = _session.get_routes();
222
223                 RouteList rl_with_auditioner = *rl;
224                 rl_with_auditioner.push_back (_session.the_auditioner());
225
226 //              for (i = dsl->begin(); i != dsl->end(); ++i) {
227 //                      cerr << "BEFORE " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl;
228 //              }
229
230                 for (i = rl_with_auditioner.begin(); !transport_work_requested() && should_run && i != rl_with_auditioner.end(); ++i) {
231
232                         boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
233
234                         if (!tr) {
235                                 continue;
236                         }
237
238                         boost::shared_ptr<IO> io = tr->input ();
239
240                         if (io && !io->active()) {
241                                 /* don't read inactive tracks */
242                                 continue;
243                         }
244
245                         switch (tr->do_refill ()) {
246                         case 0:
247                                 break;
248                                 
249                         case 1:
250                                 disk_work_outstanding = true;
251                                 break;
252
253                         default:
254                                 error << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << endmsg;
255                                 break;
256                         }
257
258                 }
259
260                 if (i != rl_with_auditioner.begin() && i != rl_with_auditioner.end()) {
261                         /* we didn't get to all the streams */
262                         disk_work_outstanding = true;
263                 }
264
265                 if (!err && transport_work_requested()) {
266                         goto restart;
267                 }
268
269                 for (i = rl->begin(); !transport_work_requested() && should_run && i != rl->end(); ++i) {
270                         // cerr << "write behind for " << (*i)->name () << endl;
271
272                         boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
273
274                         if (!tr) {
275                                 continue;
276                         }
277
278                         /* note that we still try to flush diskstreams attached to inactive routes
279                          */
280
281                         switch (tr->do_flush (ButlerContext)) {
282                         case 0:
283                                 break;
284                                 
285                         case 1:
286                                 disk_work_outstanding = true;
287                                 break;
288
289                         default:
290                                 err++;
291                                 error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
292                                 /* don't break - try to flush all streams in case they
293                                    are split across disks.
294                                 */
295                         }
296                 }
297
298                 if (err && _session.actively_recording()) {
299                         /* stop the transport and try to catch as much possible
300                            captured state as we can.
301                         */
302                         _session.request_stop ();
303                 }
304
305                 if (i != rl->begin() && i != rl->end()) {
306                         /* we didn't get to all the streams */
307                         disk_work_outstanding = true;
308                 }
309
310                 if (!err && transport_work_requested()) {
311                         goto restart;
312                 }
313
314                 if (!disk_work_outstanding) {
315                         _session.refresh_disk_space ();
316                 }
317
318
319                 {
320                         Glib::Mutex::Lock lm (request_lock);
321
322                         if (should_run && (disk_work_outstanding || transport_work_requested())) {
323 //                              for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
324 //                                      cerr << "AFTER " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl;
325 //                              }
326
327                                 goto restart;
328                         }
329
330                         paused.signal();
331                 }
332
333                 empty_pool_trash ();
334         }
335
336         pthread_exit_pbd (0);
337         /*NOTREACHED*/
338         return (0);
339 }
340
341 void
342 Butler::schedule_transport_work ()
343 {
344         g_atomic_int_inc (&should_do_transport_work);
345         summon ();
346 }
347
348 void
349 Butler::summon ()
350 {
351         char c = Request::Run;
352         (void) ::write (request_pipe[1], &c, 1);
353 }
354
355 void
356 Butler::stop ()
357 {
358         Glib::Mutex::Lock lm (request_lock);
359         char c = Request::Pause;
360         (void) ::write (request_pipe[1], &c, 1);
361         paused.wait(request_lock);
362 }
363
364 void
365 Butler::wait_until_finished ()
366 {
367         Glib::Mutex::Lock lm (request_lock);
368         char c = Request::Wake;
369         (void) ::write (request_pipe[1], &c, 1);
370         paused.wait(request_lock);
371 }
372
373 bool
374 Butler::transport_work_requested () const
375 {
376         return g_atomic_int_get(&should_do_transport_work);
377 }
378
379 void
380 Butler::empty_pool_trash ()
381 {
382         /* look in the trash, deleting empty pools until we come to one that is not empty */
383
384         RingBuffer<CrossThreadPool*>::rw_vector vec;
385         pool_trash.get_read_vector (&vec);
386
387         guint deleted = 0;
388
389         for (int i = 0; i < 2; ++i) {
390                 for (guint j = 0; j < vec.len[i]; ++j) {
391                         if (vec.buf[i][j]->empty()) {
392                                 delete vec.buf[i][j];
393                                 ++deleted;
394                         } else {
395                                 /* found a non-empty pool, so stop deleting */
396                                 if (deleted) {
397                                         pool_trash.increment_read_idx (deleted);
398                                 }
399                                 return;
400                         }
401                 }
402         }
403
404         if (deleted) {
405                 pool_trash.increment_read_idx (deleted);
406         }
407 }
408
409 void
410 Butler::drop_references ()
411 {
412         SessionEvent::pool->set_trash (0);
413 }
414
415
416 } // namespace ARDOUR
417