Use PBD::GlibSemaphore in Butler to signal requests on windows
[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 WIN32
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 WIN32
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 WIN32
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 WIN32
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 WIN32
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 WIN32
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                                                 pthread_exit_pbd (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         pthread_exit_pbd (0);
376         /*NOTREACHED*/
377         return (0);
378 }
379
380 void
381 Butler::schedule_transport_work ()
382 {
383         g_atomic_int_inc (&should_do_transport_work);
384         summon ();
385 }
386
387 void
388 Butler::queue_request (Request::Type r)
389 {
390 #ifndef WIN32
391         char c = r;
392         (void) ::write (request_pipe[1], &c, 1);
393 #else
394         m_request_state.set (r);
395         m_request_sem.post ();
396 #endif
397 }
398
399 void
400 Butler::summon ()
401 {
402         queue_request (Request::Run);
403 }
404
405 void
406 Butler::stop ()
407 {
408         Glib::Threads::Mutex::Lock lm (request_lock);
409         queue_request (Request::Pause);
410         paused.wait(request_lock);
411 }
412
413 void
414 Butler::wait_until_finished ()
415 {
416         Glib::Threads::Mutex::Lock lm (request_lock);
417         queue_request (Request::Pause);
418         paused.wait(request_lock);
419 }
420
421 bool
422 Butler::transport_work_requested () const
423 {
424         return g_atomic_int_get(&should_do_transport_work);
425 }
426
427 void
428 Butler::empty_pool_trash ()
429 {
430         /* look in the trash, deleting empty pools until we come to one that is not empty */
431
432         RingBuffer<CrossThreadPool*>::rw_vector vec;
433         pool_trash.get_read_vector (&vec);
434
435         guint deleted = 0;
436
437         for (int i = 0; i < 2; ++i) {
438                 for (guint j = 0; j < vec.len[i]; ++j) {
439                         if (vec.buf[i][j]->empty()) {
440                                 delete vec.buf[i][j];
441                                 ++deleted;
442                         } else {
443                                 /* found a non-empty pool, so stop deleting */
444                                 if (deleted) {
445                                         pool_trash.increment_read_idx (deleted);
446                                 }
447                                 return;
448                         }
449                 }
450         }
451
452         if (deleted) {
453                 pool_trash.increment_read_idx (deleted);
454         }
455 }
456
457 void
458 Butler::drop_references ()
459 {
460         SessionEvent::pool->set_trash (0);
461 }
462
463
464 } // namespace ARDOUR
465