ardour
butler.cc
Go to the documentation of this file.
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/debug.h"
31 #include "ardour/butler.h"
32 #include "ardour/io.h"
33 #include "ardour/midi_diskstream.h"
34 #include "ardour/session.h"
35 #include "ardour/track.h"
36 #include "ardour/auditioner.h"
37 
38 #include "i18n.h"
39 
40 using namespace PBD;
41 
42 namespace ARDOUR {
43 
45  : SessionHandleRef (s)
46  , thread()
47  , have_thread (false)
48  , audio_dstream_capture_buffer_size(0)
49  , audio_dstream_playback_buffer_size(0)
50  , midi_dstream_buffer_size(0)
51  , pool_trash(16)
52  , _xthread (true)
53 {
54  g_atomic_int_set(&should_do_transport_work, 0);
56 
57  Config->ParameterChanged.connect_same_thread (*this, boost::bind (&Butler::config_changed, this, _1));
58 }
59 
61 {
63 }
64 
65 void
66 Butler::config_changed (std::string p)
67 {
68  if (p == "playback-buffer-seconds") {
69  /* size is in Samples, not bytes */
70  audio_dstream_playback_buffer_size = (uint32_t) floor (Config->get_audio_playback_buffer_seconds() * _session.frame_rate());
72  } else if (p == "capture-buffer-seconds") {
73  audio_dstream_capture_buffer_size = (uint32_t) floor (Config->get_audio_capture_buffer_seconds() * _session.frame_rate());
75  } else if (p == "midi-readahead") {
77  }
78 }
79 
80 int
82 {
83  const float rate = (float)_session.frame_rate();
84 
85  /* size is in Samples, not bytes */
86  audio_dstream_capture_buffer_size = (uint32_t) floor (Config->get_audio_capture_buffer_seconds() * rate);
87  audio_dstream_playback_buffer_size = (uint32_t) floor (Config->get_audio_playback_buffer_seconds() * rate);
88 
89  /* size is in bytes
90  * XXX: Jack needs to tell us the MIDI buffer size
91  * (i.e. how many MIDI bytes we might see in a cycle)
92  */
93  midi_dstream_buffer_size = (uint32_t) floor (Config->get_midi_track_buffer_seconds() * rate);
94 
95  MidiDiskstream::set_readahead_frames ((framecnt_t) (Config->get_midi_readahead() * rate));
96 
97  should_run = false;
98 
99  if (pthread_create_and_store ("disk butler", &thread, _thread_work, this)) {
100  error << _("Session: could not create butler thread") << endmsg;
101  return -1;
102  }
103 
104  //pthread_detach (thread);
105  have_thread = true;
106  return 0;
107 }
108 
109 void
111 {
112  if (have_thread) {
113  void* status;
114  DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: ask butler to quit @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
116  pthread_join (thread, &status);
117  }
118 }
119 
120 void *
122 {
123  SessionEvent::create_per_thread_pool ("butler events", 4096);
124  pthread_set_name (X_("butler"));
125  return ((Butler *) arg)->thread_work ();
126 }
127 
128 void *
130 {
131  uint32_t err = 0;
132 
133  bool disk_work_outstanding = false;
134  RouteList::iterator i;
135 
136  while (true) {
137  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()));
138 
139  if(!disk_work_outstanding) {
140  DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 butler waits for requests @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
141 
142  char msg;
143  /* empty the pipe of all current requests */
144  if (_xthread.receive (msg, true) >= 0) {
145  Request::Type req = (Request::Type) msg;
146  switch (req) {
147 
148  case Request::Run:
149  DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler asked to run @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
150  should_run = true;
151  break;
152 
153  case Request::Pause:
154  DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler asked to pause @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
155  should_run = false;
156  break;
157 
158  case Request::Quit:
159  DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler asked to quit @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
160  return 0;
161  abort(); /*NOTREACHED*/
162  break;
163 
164  default:
165  break;
166  }
167  }
168  }
169 
170 
171  restart:
172  DEBUG_TRACE (DEBUG::Butler, "at restart for disk work\n");
173  disk_work_outstanding = false;
174 
175  if (transport_work_requested()) {
176  DEBUG_TRACE (DEBUG::Butler, string_compose ("do transport work @ %1\n", g_get_monotonic_time()));
178  DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttransport work complete @ %1\n", g_get_monotonic_time()));
179  }
180 
181  frameoffset_t audition_seek;
183  && (audition_seek = _session.the_auditioner()->seek_frame()) >= 0) {
185  DEBUG_TRACE (DEBUG::Butler, "seek the auditioner\n");
186  tr->seek(audition_seek);
187  _session.the_auditioner()->seek_response(audition_seek);
188  }
189 
191 
192  RouteList rl_with_auditioner = *rl;
193  rl_with_auditioner.push_back (_session.the_auditioner());
194 
195  for (i = rl_with_auditioner.begin(); !transport_work_requested() && should_run && i != rl_with_auditioner.end(); ++i) {
196 
198 
199  if (!tr) {
200  continue;
201  }
202 
203  boost::shared_ptr<IO> io = tr->input ();
204 
205  if (io && !io->active()) {
206  /* don't read inactive tracks */
207  DEBUG_TRACE (DEBUG::Butler, string_compose ("butler skips inactive track %1\n", tr->name()));
208  continue;
209  }
210  DEBUG_TRACE (DEBUG::Butler, string_compose ("butler refills %1, playback load = %2\n", tr->name(), tr->playback_buffer_load()));
211  switch (tr->do_refill ()) {
212  case 0:
213  DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill done %1\n", tr->name()));
214  break;
215 
216  case 1:
217  DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill unfinished %1\n", tr->name()));
218  disk_work_outstanding = true;
219  break;
220 
221  default:
222  error << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << endmsg;
223  std::cerr << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << std::endl;
224  break;
225  }
226 
227  }
228 
229  if (i != rl_with_auditioner.begin() && i != rl_with_auditioner.end()) {
230  /* we didn't get to all the streams */
231  disk_work_outstanding = true;
232  }
233 
234  if (!err && transport_work_requested()) {
235  DEBUG_TRACE (DEBUG::Butler, "transport work requested during refill, back to restart\n");
236  goto restart;
237  }
238 
239  for (i = rl->begin(); !transport_work_requested() && should_run && i != rl->end(); ++i) {
240  // cerr << "write behind for " << (*i)->name () << endl;
241 
243 
244  if (!tr) {
245  continue;
246  }
247 
248  /* note that we still try to flush diskstreams attached to inactive routes
249  */
250 
251  gint64 before, after;
252  int ret;
253 
254  DEBUG_TRACE (DEBUG::Butler, string_compose ("butler flushes track %1 capture load %2\n", tr->name(), tr->capture_buffer_load()));
255  before = g_get_monotonic_time ();
256  ret = tr->do_flush (ButlerContext);
257  after = g_get_monotonic_time ();
258  switch (ret) {
259  case 0:
260  DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush complete for %1, %2 usecs\n", tr->name(), after - before));
261  break;
262 
263  case 1:
264  DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush not finished for %1, %2 usecs\n", tr->name(), after - before));
265  disk_work_outstanding = true;
266  break;
267 
268  default:
269  err++;
270  error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
271  std::cerr << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << std::endl;
272  /* don't break - try to flush all streams in case they
273  are split across disks.
274  */
275  }
276  }
277 
278  if (err && _session.actively_recording()) {
279  /* stop the transport and try to catch as much possible
280  captured state as we can.
281  */
282  DEBUG_TRACE (DEBUG::Butler, "error occurred during recording - stop transport\n");
284  }
285 
286  if (i != rl->begin() && i != rl->end()) {
287  /* we didn't get to all the streams */
288  DEBUG_TRACE (DEBUG::Butler, "not all tracks processed, will need to go back for more\n");
289  disk_work_outstanding = true;
290  }
291 
292  if (!err && transport_work_requested()) {
293  DEBUG_TRACE (DEBUG::Butler, "transport work requested during flush, back to restart\n");
294  goto restart;
295  }
296 
297  if (!disk_work_outstanding) {
299  }
300 
301 
302  {
304 
305  if (should_run && (disk_work_outstanding || transport_work_requested())) {
306  DEBUG_TRACE (DEBUG::Butler, string_compose ("at end, should run %1 disk work %2 transport work %3 ... goto restart\n",
307  should_run, disk_work_outstanding, transport_work_requested()));
308  goto restart;
309  }
310 
311  DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler signals pause @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
312  paused.signal();
313  }
314 
315  DEBUG_TRACE (DEBUG::Butler, "butler emptying pool trash\n");
316  empty_pool_trash ();
317  }
318 
319  return (0);
320 }
321 
322 void
324 {
325  g_atomic_int_inc (&should_do_transport_work);
326  summon ();
327 }
328 
329 void
331 {
332  char c = r;
333  if (_xthread.deliver (c) != 1) {
334  /* the x-thread channel is non-blocking
335  * write may fail, but we really don't want to wait
336  * under normal circumstances.
337  *
338  * a lost "run" requests under normal RT operation
339  * is mostly harmless.
340  *
341  * TODO if ardour is freehweeling, wait & retry.
342  * ditto for Request::Type Quit
343  */
344  assert(1); // we're screwd
345  }
346 }
347 
348 void
350 {
351  DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: summon butler to run @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
353 }
354 
355 void
357 {
359  DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: asking butler to stop @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
361  paused.wait(request_lock);
362 }
363 
364 void
366 {
368  DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: waiting for butler to finish @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
370  paused.wait(request_lock);
371 }
372 
373 bool
375 {
376  return g_atomic_int_get(&should_do_transport_work);
377 }
378 
379 void
381 {
382  /* look in the trash, deleting empty pools until we come to one that is not empty */
383 
386 
387  guint deleted = 0;
388 
389  for (int i = 0; i < 2; ++i) {
390  for (guint j = 0; j < vec.len[i]; ++j) {
391  if (vec.buf[i][j]->empty()) {
392  delete vec.buf[i][j];
393  ++deleted;
394  } else {
395  /* found a non-empty pool, so stop deleting */
396  if (deleted) {
397  pool_trash.increment_read_idx (deleted);
398  }
399  return;
400  }
401  }
402  }
403 
404  if (deleted) {
405  pool_trash.increment_read_idx (deleted);
406  }
407 }
408 
409 void
411 {
412  std::cerr << "Butler drops pool trash\n";
414 }
415 
416 
417 } // namespace ARDOUR
418 
void summon()
Definition: butler.cc:349
void terminate_thread()
Definition: butler.cc:110
static void * _thread_work(void *arg)
Definition: butler.cc:121
ARDOUR::Session & _session
void config_changed(std::string)
Definition: butler.cc:66
#define DEBUG_THREAD_SELF
Definition: debug.h:63
pthread_t thread
Definition: butler.h:79
bool actively_recording() const
Definition: session.h:280
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
static void create_per_thread_pool(const std::string &n, uint32_t nitems)
int receive(char &msg, bool wait=false)
void * thread_work()
Definition: butler.cc:129
bool is_auditioning() const
Definition: session.cc:4184
LIBPBD_API Transmitter error
void get_read_vector(rw_vector *)
Definition: ringbuffer.h:203
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
int start_thread()
Definition: butler.cc:81
framecnt_t frame_rate() const
Definition: session.h:365
void increment_read_idx(guint cnt)
Definition: ringbuffer.h:74
boost::shared_ptr< Auditioner > the_auditioner()
Definition: session.h:673
#define _(Text)
Definition: i18n.h:11
LIBARDOUR_API uint64_t Butler
Definition: diskstream.h:191
void request_stop(bool abort=false, bool clear_state=false)
#define X_(Text)
Definition: i18n.h:13
int64_t framecnt_t
Definition: types.h:76
LIBARDOUR_API RCConfiguration * Config
Definition: globals.cc:119
void schedule_transport_work()
Definition: butler.cc:323
void adjust_capture_buffering()
Definition: amp.h:29
framecnt_t audio_dstream_capture_buffer_size
Definition: butler.h:85
RingBuffer< CrossThreadPool * > pool_trash
Definition: butler.h:88
bool have_thread
Definition: butler.h:80
static void set_readahead_frames(framecnt_t frames_ahead)
boost::shared_ptr< RouteList > get_routes() const
Definition: session.h:229
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
LIBPBD_API void pthread_set_name(const char *name)
int64_t frameoffset_t
Definition: types.h:71
bool should_run
Definition: butler.h:83
PBD::Signal1< void, std::string > ParameterChanged
Definition: configuration.h:44
bool empty()
Definition: pool.cc:269
Glib::Threads::Cond paused
Definition: butler.h:82
uint32_t midi_dstream_buffer_size
Definition: butler.h:87
void set_trash(RingBuffer< CrossThreadPool * > *t)
Definition: pool.cc:193
void empty_pool_trash()
Definition: butler.cc:380
Glib::Threads::Mutex request_lock
Definition: butler.h:81
Definition: debug.h:30
void drop_references()
Definition: butler.cc:410
bool transport_work_requested() const
Definition: butler.cc:374
CrossThreadChannel _xthread
Definition: butler.h:99
void wait_until_finished()
Definition: butler.cc:365
void queue_request(Request::Type r)
Definition: butler.cc:330
static PerThreadPool * pool
std::list< boost::shared_ptr< Route > > RouteList
Definition: types.h:532
LIBPBD_API int pthread_create_and_store(std::string name, pthread_t *thread, void *(*start_routine)(void *), void *arg)
void stop()
Definition: butler.cc:356
gint should_do_transport_work
Definition: butler.h:84
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
void adjust_playback_buffering()
framecnt_t audio_dstream_playback_buffer_size
Definition: butler.h:86