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