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