fix merge conflicts from 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         , 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                 boost::shared_ptr<RouteList> rl = _session.get_routes();
261
262                 RouteList rl_with_auditioner = *rl;
263                 rl_with_auditioner.push_back (_session.the_auditioner());
264
265 //              for (i = dsl->begin(); i != dsl->end(); ++i) {
266 //                      cerr << "BEFORE " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl;
267 //              }
268
269                 for (i = rl_with_auditioner.begin(); !transport_work_requested() && should_run && i != rl_with_auditioner.end(); ++i) {
270
271                         boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
272
273                         if (!tr) {
274                                 continue;
275                         }
276
277                         boost::shared_ptr<IO> io = tr->input ();
278
279                         if (io && !io->active()) {
280                                 /* don't read inactive tracks */
281                                 continue;
282                         }
283
284                         switch (tr->do_refill ()) {
285                         case 0:
286                                 break;
287                                 
288                         case 1:
289                                 disk_work_outstanding = true;
290                                 break;
291
292                         default:
293                                 error << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << endmsg;
294                                 break;
295                         }
296
297                 }
298
299                 if (i != rl_with_auditioner.begin() && i != rl_with_auditioner.end()) {
300                         /* we didn't get to all the streams */
301                         disk_work_outstanding = true;
302                 }
303
304                 if (!err && transport_work_requested()) {
305                         goto restart;
306                 }
307
308                 for (i = rl->begin(); !transport_work_requested() && should_run && i != rl->end(); ++i) {
309                         // cerr << "write behind for " << (*i)->name () << endl;
310
311                         boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
312
313                         if (!tr) {
314                                 continue;
315                         }
316
317                         /* note that we still try to flush diskstreams attached to inactive routes
318                          */
319
320                         switch (tr->do_flush (ButlerContext)) {
321                         case 0:
322                                 break;
323                                 
324                         case 1:
325                                 disk_work_outstanding = true;
326                                 break;
327
328                         default:
329                                 err++;
330                                 error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
331                                 /* don't break - try to flush all streams in case they
332                                    are split across disks.
333                                 */
334                         }
335                 }
336
337                 if (err && _session.actively_recording()) {
338                         /* stop the transport and try to catch as much possible
339                            captured state as we can.
340                         */
341                         _session.request_stop ();
342                 }
343
344                 if (i != rl->begin() && i != rl->end()) {
345                         /* we didn't get to all the streams */
346                         disk_work_outstanding = true;
347                 }
348
349                 if (!err && transport_work_requested()) {
350                         goto restart;
351                 }
352
353                 if (!disk_work_outstanding) {
354                         _session.refresh_disk_space ();
355                 }
356
357
358                 {
359                         Glib::Threads::Mutex::Lock lm (request_lock);
360
361                         if (should_run && (disk_work_outstanding || transport_work_requested())) {
362 //                              for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
363 //                                      cerr << "AFTER " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl;
364 //                              }
365
366                                 goto restart;
367                         }
368
369                         paused.signal();
370                 }
371
372                 empty_pool_trash ();
373         }
374
375         return (0);
376 }
377
378 void
379 Butler::schedule_transport_work ()
380 {
381         g_atomic_int_inc (&should_do_transport_work);
382         summon ();
383 }
384
385 void
386 Butler::queue_request (Request::Type r)
387 {
388 #ifndef PLATFORM_WINDOWS
389         char c = r;
390         (void) ::write (request_pipe[1], &c, 1);
391 #else
392         m_request_state.set (r);
393         m_request_sem.post ();
394 #endif
395 }
396
397 void
398 Butler::summon ()
399 {
400         queue_request (Request::Run);
401 }
402
403 void
404 Butler::stop ()
405 {
406         Glib::Threads::Mutex::Lock lm (request_lock);
407         queue_request (Request::Pause);
408         paused.wait(request_lock);
409 }
410
411 void
412 Butler::wait_until_finished ()
413 {
414         Glib::Threads::Mutex::Lock lm (request_lock);
415         queue_request (Request::Pause);
416         paused.wait(request_lock);
417 }
418
419 bool
420 Butler::transport_work_requested () const
421 {
422         return g_atomic_int_get(&should_do_transport_work);
423 }
424
425 void
426 Butler::empty_pool_trash ()
427 {
428         /* look in the trash, deleting empty pools until we come to one that is not empty */
429
430         RingBuffer<CrossThreadPool*>::rw_vector vec;
431         pool_trash.get_read_vector (&vec);
432
433         guint deleted = 0;
434
435         for (int i = 0; i < 2; ++i) {
436                 for (guint j = 0; j < vec.len[i]; ++j) {
437                         if (vec.buf[i][j]->empty()) {
438                                 delete vec.buf[i][j];
439                                 ++deleted;
440                         } else {
441                                 /* found a non-empty pool, so stop deleting */
442                                 if (deleted) {
443                                         pool_trash.increment_read_idx (deleted);
444                                 }
445                                 return;
446                         }
447                 }
448         }
449
450         if (deleted) {
451                 pool_trash.increment_read_idx (deleted);
452         }
453 }
454
455 void
456 Butler::drop_references ()
457 {
458         cerr << "Butler drops pool trash\n";
459         SessionEvent::pool->set_trash (0);
460 }
461
462
463 } // namespace ARDOUR
464