dd83707e68aad6f754b9a2fb7aed603d2dec185f
[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
24 #ifndef PLATFORM_WINDOWS
25 #include <poll.h>
26 #endif
27
28 #include "pbd/error.h"
29 #include "pbd/pthread_utils.h"
30
31 #include "ardour/butler.h"
32 #include "ardour/debug.h"
33 #include "ardour/disk_io.h"
34 #include "ardour/disk_reader.h"
35 #include "ardour/io.h"
36 #include "ardour/session.h"
37 #include "ardour/track.h"
38 #include "ardour/auditioner.h"
39
40 #include "pbd/i18n.h"
41
42 using namespace PBD;
43
44 namespace ARDOUR {
45
46 Butler::Butler(Session& s)
47         : SessionHandleRef (s)
48         , thread()
49         , have_thread (false)
50         , audio_dstream_capture_buffer_size(0)
51         , audio_dstream_playback_buffer_size(0)
52         , midi_dstream_buffer_size(0)
53         , pool_trash(16)
54         , _xthread (true)
55 {
56         g_atomic_int_set(&should_do_transport_work, 0);
57         SessionEvent::pool->set_trash (&pool_trash);
58
59         /* catch future changes to parameters */
60         Config->ParameterChanged.connect_same_thread (*this, boost::bind (&Butler::config_changed, this, _1));
61 }
62
63 Butler::~Butler()
64 {
65         terminate_thread ();
66 }
67
68 void
69 Butler::map_parameters ()
70 {
71         /* use any current ones that we care about */
72         boost::function<void (std::string)> ff (boost::bind (&Butler::config_changed, this, _1));
73         Config->map_parameters (ff);
74 }
75
76 void
77 Butler::config_changed (std::string p)
78 {
79         if (p == "playback-buffer-seconds") {
80                 _session.adjust_playback_buffering ();
81                 if (Config->get_buffering_preset() == Custom) {
82                         /* size is in Samples, not bytes */
83                         audio_dstream_playback_buffer_size = (uint32_t) floor (Config->get_audio_playback_buffer_seconds() * _session.frame_rate());
84                         _session.adjust_playback_buffering ();
85                 } else {
86 #ifndef NDEBUG
87                         std::cerr << "Skip explicit buffer seconds, preset in use\n";
88 #endif
89                 }
90         } else if (p == "capture-buffer-seconds") {
91                 if (Config->get_buffering_preset() == Custom) {
92                         audio_dstream_capture_buffer_size = (uint32_t) floor (Config->get_audio_capture_buffer_seconds() * _session.frame_rate());
93                         _session.adjust_capture_buffering ();
94                 } else {
95 #ifndef NDEBUG
96                         std::cerr << "Skip explicit buffer seconds, preset in use\n";
97 #endif
98                 }
99         } else if (p == "buffering-preset") {
100                 DiskIOProcessor::set_buffering_parameters (Config->get_buffering_preset());
101                 audio_dstream_capture_buffer_size = (uint32_t) floor (Config->get_audio_capture_buffer_seconds() * _session.frame_rate());
102                 audio_dstream_playback_buffer_size = (uint32_t) floor (Config->get_audio_playback_buffer_seconds() * _session.frame_rate());
103                 _session.adjust_capture_buffering ();
104                 _session.adjust_playback_buffering ();
105         } else if (p == "midi-readahead") {
106                 DiskReader::set_midi_readahead_frames ((framecnt_t) (Config->get_midi_readahead() * _session.frame_rate()));
107         }
108 }
109
110 int
111 Butler::start_thread()
112 {
113         // set up capture and playback buffering
114         DiskIOProcessor::set_buffering_parameters (Config->get_buffering_preset());
115
116         /* size is in Samples, not bytes */
117         const float rate = (float)_session.frame_rate();
118         audio_dstream_capture_buffer_size = (uint32_t) floor (Config->get_audio_capture_buffer_seconds() * rate);
119         audio_dstream_playback_buffer_size = (uint32_t) floor (Config->get_audio_playback_buffer_seconds() * rate);
120
121         /* size is in bytes
122          * XXX: AudioEngine needs to tell us the MIDI buffer size
123          * (i.e. how many MIDI bytes we might see in a cycle)
124          */
125         midi_dstream_buffer_size = (uint32_t) floor (Config->get_midi_track_buffer_seconds() * rate);
126
127         DiskReader::set_midi_readahead_frames ((framecnt_t) (Config->get_midi_readahead() * rate));
128
129         should_run = false;
130
131         if (pthread_create_and_store ("disk butler", &thread, _thread_work, this)) {
132                 error << _("Session: could not create butler thread") << endmsg;
133                 return -1;
134         }
135
136         //pthread_detach (thread);
137         have_thread = true;
138
139         // we are ready to request buffer adjustments
140         _session.adjust_capture_buffering ();
141         _session.adjust_playback_buffering ();
142
143         return 0;
144 }
145
146 void
147 Butler::terminate_thread ()
148 {
149         if (have_thread) {
150                 void* status;
151                 DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: ask butler to quit @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
152                 queue_request (Request::Quit);
153                 pthread_join (thread, &status);
154         }
155 }
156
157 void *
158 Butler::_thread_work (void* arg)
159 {
160         SessionEvent::create_per_thread_pool ("butler events", 4096);
161         pthread_set_name (X_("butler"));
162         return ((Butler *) arg)->thread_work ();
163 }
164
165 void *
166 Butler::thread_work ()
167 {
168         uint32_t err = 0;
169
170         bool disk_work_outstanding = false;
171         RouteList::iterator i;
172
173         while (true) {
174                 DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 butler main loop, disk work outstanding ? %2 @ %3\n", DEBUG_THREAD_SELF, disk_work_outstanding, g_get_monotonic_time()));
175
176                 if(!disk_work_outstanding) {
177                         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 butler waits for requests @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
178
179                         char msg;
180                         /* empty the pipe of all current requests */
181                         if (_xthread.receive (msg, true) >= 0) {
182                                 Request::Type req = (Request::Type) msg;
183                                 switch (req) {
184
185                                         case Request::Run:
186                                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler asked to run @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
187                                                 should_run = true;
188                                                 break;
189
190                                         case Request::Pause:
191                                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler asked to pause @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
192                                                 should_run = false;
193                                                 break;
194
195                                         case Request::Quit:
196                                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler asked to quit @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
197                                                 return 0;
198                                                 abort(); /*NOTREACHED*/
199                                                 break;
200
201                                         default:
202                                                 break;
203                                 }
204                         }
205                 }
206
207
208           restart:
209                 DEBUG_TRACE (DEBUG::Butler, "at restart for disk work\n");
210                 disk_work_outstanding = false;
211
212                 if (transport_work_requested()) {
213                         DEBUG_TRACE (DEBUG::Butler, string_compose ("do transport work @ %1\n", g_get_monotonic_time()));
214                         _session.butler_transport_work ();
215                         DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttransport work complete @ %1, twr = %2\n", g_get_monotonic_time(), transport_work_requested()));
216                 }
217
218                 frameoffset_t audition_seek;
219                 if (should_run && _session.is_auditioning()
220                                 && (audition_seek = _session.the_auditioner()->seek_frame()) >= 0) {
221                         boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (_session.the_auditioner());
222                         DEBUG_TRACE (DEBUG::Butler, "seek the auditioner\n");
223                         tr->seek(audition_seek);
224                         tr->do_refill ();
225                         _session.the_auditioner()->seek_response(audition_seek);
226                 }
227
228                 boost::shared_ptr<RouteList> rl = _session.get_routes();
229
230                 RouteList rl_with_auditioner = *rl;
231                 rl_with_auditioner.push_back (_session.the_auditioner());
232
233                 for (i = rl_with_auditioner.begin(); !transport_work_requested() && should_run && i != rl_with_auditioner.end(); ++i) {
234
235                         boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
236
237                         if (!tr) {
238                                 continue;
239                         }
240
241                         boost::shared_ptr<IO> io = tr->input ();
242
243                         if (io && !io->active()) {
244                                 /* don't read inactive tracks */
245                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("butler skips inactive track %1\n", tr->name()));
246                                 continue;
247                         }
248                         DEBUG_TRACE (DEBUG::Butler, string_compose ("butler refills %1, playback load = %2\n", tr->name(), tr->playback_buffer_load()));
249                         switch (tr->do_refill ()) {
250                         case 0:
251                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill done %1\n", tr->name()));
252                                 break;
253
254                         case 1:
255                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill unfinished %1\n", tr->name()));
256                                 disk_work_outstanding = true;
257                                 break;
258
259                         default:
260                                 error << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << endmsg;
261                                 std::cerr << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << std::endl;
262                                 break;
263                         }
264
265                 }
266
267                 if (i != rl_with_auditioner.begin() && i != rl_with_auditioner.end()) {
268                         /* we didn't get to all the streams */
269                         disk_work_outstanding = true;
270                 }
271
272                 if (!err && transport_work_requested()) {
273                         DEBUG_TRACE (DEBUG::Butler, "transport work requested during refill, back to restart\n");
274                         goto restart;
275                 }
276
277                 disk_work_outstanding = flush_tracks_to_disk_normal (rl, err);
278
279                 if (err && _session.actively_recording()) {
280                         /* stop the transport and try to catch as much possible
281                            captured state as we can.
282                         */
283                         DEBUG_TRACE (DEBUG::Butler, "error occurred during recording - stop transport\n");
284                         _session.request_stop ();
285                 }
286
287                 if (!err && transport_work_requested()) {
288                         DEBUG_TRACE (DEBUG::Butler, "transport work requested during flush, back to restart\n");
289                         goto restart;
290                 }
291
292                 if (!disk_work_outstanding) {
293                         _session.refresh_disk_space ();
294                 }
295
296                 {
297                         Glib::Threads::Mutex::Lock lm (request_lock);
298
299                         if (should_run && (disk_work_outstanding || transport_work_requested())) {
300                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("at end, should run %1 disk work %2 transport work %3 ... goto restart\n",
301                                                                             should_run, disk_work_outstanding, transport_work_requested()));
302                                 goto restart;
303                         }
304
305                         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler signals pause @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
306                         paused.signal();
307                 }
308
309                 DEBUG_TRACE (DEBUG::Butler, "butler emptying pool trash\n");
310                 empty_pool_trash ();
311         }
312
313         return (0);
314 }
315
316 bool
317 Butler::flush_tracks_to_disk_normal (boost::shared_ptr<RouteList> rl, uint32_t& errors)
318 {
319         bool disk_work_outstanding = false;
320
321         for (RouteList::iterator i = rl->begin(); !transport_work_requested() && should_run && i != rl->end(); ++i) {
322
323                 // cerr << "write behind for " << (*i)->name () << endl;
324
325                 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
326
327                 if (!tr) {
328                         continue;
329                 }
330
331                 /* note that we still try to flush diskstreams attached to inactive routes
332                  */
333
334                 int ret;
335
336                 DEBUG_TRACE (DEBUG::Butler, string_compose ("butler flushes track %1 capture load %2\n", tr->name(), tr->capture_buffer_load()));
337                 ret = tr->do_flush (ButlerContext, false);
338                 switch (ret) {
339                 case 0:
340                         DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush complete for %1\n", tr->name()));
341                         break;
342
343                 case 1:
344                         DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush not finished for %1\n", tr->name()));
345                         disk_work_outstanding = true;
346                         break;
347
348                 default:
349                         errors++;
350                         error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
351                         std::cerr << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << std::endl;
352                         /* don't break - try to flush all streams in case they
353                            are split across disks.
354                         */
355                 }
356         }
357
358         return disk_work_outstanding;
359 }
360
361 bool
362 Butler::flush_tracks_to_disk_after_locate (boost::shared_ptr<RouteList> rl, uint32_t& errors)
363 {
364         bool disk_work_outstanding = false;
365
366         /* almost the same as the "normal" version except that we do not test
367          * for transport_work_requested() and we force flushes.
368          */
369
370         for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
371
372                 // cerr << "write behind for " << (*i)->name () << endl;
373
374                 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
375
376                 if (!tr) {
377                         continue;
378                 }
379
380                 /* note that we still try to flush diskstreams attached to inactive routes
381                  */
382
383                 int ret;
384
385                 DEBUG_TRACE (DEBUG::Butler, string_compose ("butler flushes track %1 capture load %2\n", tr->name(), tr->capture_buffer_load()));
386                 ret = tr->do_flush (ButlerContext, true);
387                 switch (ret) {
388                 case 0:
389                         DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush complete for %1\n", tr->name()));
390                         break;
391
392                 case 1:
393                         DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush not finished for %1\n", tr->name()));
394                         disk_work_outstanding = true;
395                         break;
396
397                 default:
398                         errors++;
399                         error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
400                         std::cerr << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << std::endl;
401                         /* don't break - try to flush all streams in case they
402                            are split across disks.
403                         */
404                 }
405         }
406
407         return disk_work_outstanding;
408 }
409
410 void
411 Butler::schedule_transport_work ()
412 {
413         g_atomic_int_inc (&should_do_transport_work);
414         summon ();
415 }
416
417 void
418 Butler::queue_request (Request::Type r)
419 {
420         char c = r;
421         if (_xthread.deliver (c) != 1) {
422                 /* the x-thread channel is non-blocking
423                  * write may fail, but we really don't want to wait
424                  * under normal circumstances.
425                  *
426                  * a lost "run" requests under normal RT operation
427                  * is mostly harmless.
428                  *
429                  * TODO if ardour is freehweeling, wait & retry.
430                  * ditto for Request::Type Quit
431                  */
432                 assert(1); // we're screwd
433         }
434 }
435
436 void
437 Butler::summon ()
438 {
439         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: summon butler to run @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
440         queue_request (Request::Run);
441 }
442
443 void
444 Butler::stop ()
445 {
446         Glib::Threads::Mutex::Lock lm (request_lock);
447         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: asking butler to stop @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
448         queue_request (Request::Pause);
449         paused.wait(request_lock);
450 }
451
452 void
453 Butler::wait_until_finished ()
454 {
455         Glib::Threads::Mutex::Lock lm (request_lock);
456         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: waiting for butler to finish @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
457         queue_request (Request::Pause);
458         paused.wait(request_lock);
459 }
460
461 bool
462 Butler::transport_work_requested () const
463 {
464         return g_atomic_int_get(&should_do_transport_work);
465 }
466
467 void
468 Butler::empty_pool_trash ()
469 {
470         /* look in the trash, deleting empty pools until we come to one that is not empty */
471
472         RingBuffer<CrossThreadPool*>::rw_vector vec;
473         pool_trash.get_read_vector (&vec);
474
475         guint deleted = 0;
476
477         for (int i = 0; i < 2; ++i) {
478                 for (guint j = 0; j < vec.len[i]; ++j) {
479                         if (vec.buf[i][j]->empty()) {
480                                 delete vec.buf[i][j];
481                                 ++deleted;
482                         } else {
483                                 /* found a non-empty pool, so stop deleting */
484                                 if (deleted) {
485                                         pool_trash.increment_read_idx (deleted);
486                                 }
487                                 return;
488                         }
489                 }
490         }
491
492         if (deleted) {
493                 pool_trash.increment_read_idx (deleted);
494         }
495 }
496
497 void
498 Butler::drop_references ()
499 {
500         std::cerr << "Butler drops pool trash\n";
501         SessionEvent::pool->set_trash (0);
502 }
503
504
505 } // namespace ARDOUR
506