add new debug trace output
[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() && (audition_seek = _session.the_auditioner()->seek_frame()) >= 0) {
220                         boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (_session.the_auditioner());
221                         DEBUG_TRACE (DEBUG::Butler, "seek the auditioner\n");
222                         tr->seek(audition_seek);
223                         tr->do_refill ();
224                         _session.the_auditioner()->seek_response(audition_seek);
225                 }
226
227                 boost::shared_ptr<RouteList> rl = _session.get_routes();
228
229                 RouteList rl_with_auditioner = *rl;
230                 rl_with_auditioner.push_back (_session.the_auditioner());
231
232                 DEBUG_TRACE (DEBUG::Butler, string_compose ("butler starts refill loop, twr = %1\n", transport_work_requested()));
233
234                 for (i = rl_with_auditioner.begin(); !transport_work_requested() && should_run && i != rl_with_auditioner.end(); ++i) {
235
236                         boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
237
238                         if (!tr) {
239                                 continue;
240                         }
241
242                         boost::shared_ptr<IO> io = tr->input ();
243
244                         if (io && !io->active()) {
245                                 /* don't read inactive tracks */
246                                 // DEBUG_TRACE (DEBUG::Butler, string_compose ("butler skips inactive track %1\n", tr->name()));
247                                 continue;
248                         }
249                         // DEBUG_TRACE (DEBUG::Butler, string_compose ("butler refills %1, playback load = %2\n", tr->name(), tr->playback_buffer_load()));
250                         switch (tr->do_refill ()) {
251                         case 0:
252                                 //DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill done %1\n", tr->name()));
253                                 break;
254
255                         case 1:
256                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill unfinished %1\n", tr->name()));
257                                 disk_work_outstanding = true;
258                                 break;
259
260                         default:
261                                 error << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << endmsg;
262                                 std::cerr << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << std::endl;
263                                 break;
264                         }
265
266                 }
267
268                 if (i != rl_with_auditioner.begin() && i != rl_with_auditioner.end()) {
269                         /* we didn't get to all the streams */
270                         disk_work_outstanding = true;
271                 }
272
273                 if (!err && transport_work_requested()) {
274                         DEBUG_TRACE (DEBUG::Butler, "transport work requested during refill, back to restart\n");
275                         goto restart;
276                 }
277
278                 disk_work_outstanding = disk_work_outstanding || flush_tracks_to_disk_normal (rl, err);
279
280                 if (err && _session.actively_recording()) {
281                         /* stop the transport and try to catch as much possible
282                            captured state as we can.
283                         */
284                         DEBUG_TRACE (DEBUG::Butler, "error occurred during recording - stop transport\n");
285                         _session.request_stop ();
286                 }
287
288                 if (!err && transport_work_requested()) {
289                         DEBUG_TRACE (DEBUG::Butler, "transport work requested during flush, back to restart\n");
290                         goto restart;
291                 }
292
293                 if (!disk_work_outstanding) {
294                         _session.refresh_disk_space ();
295                 }
296
297                 {
298                         Glib::Threads::Mutex::Lock lm (request_lock);
299
300                         if (should_run && (disk_work_outstanding || transport_work_requested())) {
301                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("at end, should run %1 disk work %2 transport work %3 ... goto restart\n",
302                                                                             should_run, disk_work_outstanding, transport_work_requested()));
303                                 goto restart;
304                         }
305
306                         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler signals pause @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
307                         paused.signal();
308                 }
309
310                 DEBUG_TRACE (DEBUG::Butler, "butler emptying pool trash\n");
311                 empty_pool_trash ();
312         }
313
314         return (0);
315 }
316
317 bool
318 Butler::flush_tracks_to_disk_normal (boost::shared_ptr<RouteList> rl, uint32_t& errors)
319 {
320         bool disk_work_outstanding = false;
321
322         for (RouteList::iterator i = rl->begin(); !transport_work_requested() && should_run && i != rl->end(); ++i) {
323
324                 // cerr << "write behind for " << (*i)->name () << endl;
325
326                 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
327
328                 if (!tr) {
329                         continue;
330                 }
331
332                 /* note that we still try to flush diskstreams attached to inactive routes
333                  */
334
335                 int ret;
336
337                 // DEBUG_TRACE (DEBUG::Butler, string_compose ("butler flushes track %1 capture load %2\n", tr->name(), tr->capture_buffer_load()));
338                 ret = tr->do_flush (ButlerContext, false);
339                 switch (ret) {
340                 case 0:
341                         //DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush complete for %1\n", tr->name()));
342                         break;
343
344                 case 1:
345                         //DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush not finished for %1\n", tr->name()));
346                         disk_work_outstanding = true;
347                         break;
348
349                 default:
350                         errors++;
351                         error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
352                         std::cerr << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << std::endl;
353                         /* don't break - try to flush all streams in case they
354                            are split across disks.
355                         */
356                 }
357         }
358
359         return disk_work_outstanding;
360 }
361
362 bool
363 Butler::flush_tracks_to_disk_after_locate (boost::shared_ptr<RouteList> rl, uint32_t& errors)
364 {
365         bool disk_work_outstanding = false;
366
367         /* almost the same as the "normal" version except that we do not test
368          * for transport_work_requested() and we force flushes.
369          */
370
371         for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
372
373                 // cerr << "write behind for " << (*i)->name () << endl;
374
375                 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
376
377                 if (!tr) {
378                         continue;
379                 }
380
381                 /* note that we still try to flush diskstreams attached to inactive routes
382                  */
383
384                 int ret;
385
386                 DEBUG_TRACE (DEBUG::Butler, string_compose ("butler flushes track %1 capture load %2\n", tr->name(), tr->capture_buffer_load()));
387                 ret = tr->do_flush (ButlerContext, true);
388                 switch (ret) {
389                 case 0:
390                         DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush complete for %1\n", tr->name()));
391                         break;
392
393                 case 1:
394                         DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush not finished for %1\n", tr->name()));
395                         disk_work_outstanding = true;
396                         break;
397
398                 default:
399                         errors++;
400                         error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
401                         std::cerr << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << std::endl;
402                         /* don't break - try to flush all streams in case they
403                            are split across disks.
404                         */
405                 }
406         }
407
408         return disk_work_outstanding;
409 }
410
411 void
412 Butler::schedule_transport_work ()
413 {
414         DEBUG_TRACE (DEBUG::Butler, "requesting more transport work\n");
415         g_atomic_int_inc (&should_do_transport_work);
416         summon ();
417 }
418
419 void
420 Butler::queue_request (Request::Type r)
421 {
422         char c = r;
423         if (_xthread.deliver (c) != 1) {
424                 /* the x-thread channel is non-blocking
425                  * write may fail, but we really don't want to wait
426                  * under normal circumstances.
427                  *
428                  * a lost "run" requests under normal RT operation
429                  * is mostly harmless.
430                  *
431                  * TODO if ardour is freehweeling, wait & retry.
432                  * ditto for Request::Type Quit
433                  */
434                 assert(1); // we're screwd
435         }
436 }
437
438 void
439 Butler::summon ()
440 {
441         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: summon butler to run @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
442         queue_request (Request::Run);
443 }
444
445 void
446 Butler::stop ()
447 {
448         Glib::Threads::Mutex::Lock lm (request_lock);
449         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: asking butler to stop @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
450         queue_request (Request::Pause);
451         paused.wait(request_lock);
452 }
453
454 void
455 Butler::wait_until_finished ()
456 {
457         Glib::Threads::Mutex::Lock lm (request_lock);
458         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: waiting for butler to finish @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
459         queue_request (Request::Pause);
460         paused.wait(request_lock);
461 }
462
463 bool
464 Butler::transport_work_requested () const
465 {
466         return g_atomic_int_get(&should_do_transport_work);
467 }
468
469 void
470 Butler::empty_pool_trash ()
471 {
472         /* look in the trash, deleting empty pools until we come to one that is not empty */
473
474         RingBuffer<CrossThreadPool*>::rw_vector vec;
475         pool_trash.get_read_vector (&vec);
476
477         guint deleted = 0;
478
479         for (int i = 0; i < 2; ++i) {
480                 for (guint j = 0; j < vec.len[i]; ++j) {
481                         if (vec.buf[i][j]->empty()) {
482                                 delete vec.buf[i][j];
483                                 ++deleted;
484                         } else {
485                                 /* found a non-empty pool, so stop deleting */
486                                 if (deleted) {
487                                         pool_trash.increment_read_idx (deleted);
488                                 }
489                                 return;
490                         }
491                 }
492         }
493
494         if (deleted) {
495                 pool_trash.increment_read_idx (deleted);
496         }
497 }
498
499 void
500 Butler::drop_references ()
501 {
502         std::cerr << "Butler drops pool trash\n";
503         SessionEvent::pool->set_trash (0);
504 }
505
506
507 } // namespace ARDOUR
508