a bit more of a possible fix for NPAE problem
[ardour2.git] / libs / ardour / session_butler.cc
blob1f2cf84bd1b3c2ad568f469107c184014b55d85d
1 /*
2 Copyright (C) 1999-2002 Paul Davis
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.
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.
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.
20 #include <algorithm>
21 #include <string>
22 #include <cmath>
23 #include <cerrno>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <poll.h>
28 #include <glibmm/thread.h>
30 #include <pbd/error.h>
31 #include <pbd/pthread_utils.h>
32 #include <pbd/stacktrace.h>
34 #include <ardour/configuration.h>
35 #include <ardour/audioengine.h>
36 #include <ardour/session.h>
37 #include <ardour/audio_diskstream.h>
38 #include <ardour/crossfade.h>
39 #include <ardour/timestamps.h>
41 #include "i18n.h"
43 using namespace std;
44 using namespace ARDOUR;
45 using namespace PBD;
47 static float _read_data_rate;
48 static float _write_data_rate;
50 /* XXX put this in the right place */
52 static inline uint32_t next_power_of_two (uint32_t n)
54 --n;
55 n |= n >> 16;
56 n |= n >> 8;
57 n |= n >> 4;
58 n |= n >> 2;
59 n |= n >> 1;
60 ++n;
61 return n;
64 /*---------------------------------------------------------------------------
65 BUTLER THREAD
66 ---------------------------------------------------------------------------*/
68 int
69 Session::start_butler_thread ()
71 /* size is in Samples, not bytes */
73 dstream_buffer_size = (uint32_t) floor (Config->get_track_buffer_seconds() * (float) frame_rate());
75 Crossfade::set_buffer_size (dstream_buffer_size);
77 butler_should_run = false;
79 if (pipe (butler_request_pipe)) {
80 error << string_compose(_("Cannot create transport request signal pipe (%1)"), strerror (errno)) << endmsg;
81 return -1;
84 if (fcntl (butler_request_pipe[0], F_SETFL, O_NONBLOCK)) {
85 error << string_compose(_("UI: cannot set O_NONBLOCK on butler request pipe (%1)"), strerror (errno)) << endmsg;
86 return -1;
89 if (fcntl (butler_request_pipe[1], F_SETFL, O_NONBLOCK)) {
90 error << string_compose(_("UI: cannot set O_NONBLOCK on butler request pipe (%1)"), strerror (errno)) << endmsg;
91 return -1;
94 if (pthread_create_and_store ("disk butler", &butler_thread, 0, _butler_thread_work, this)) {
95 error << _("Session: could not create butler thread") << endmsg;
96 return -1;
99 // pthread_detach (butler_thread);
101 return 0;
104 void
105 Session::terminate_butler_thread ()
107 if (butler_thread) {
108 void* status;
109 char c = ButlerRequest::Quit;
110 ::write (butler_request_pipe[1], &c, 1);
111 pthread_join (butler_thread, &status);
115 void
116 Session::schedule_butler_transport_work ()
118 g_atomic_int_inc (&butler_should_do_transport_work);
119 summon_butler ();
122 void
123 Session::schedule_curve_reallocation ()
125 post_transport_work = PostTransportWork (post_transport_work | PostTransportCurveRealloc);
126 schedule_butler_transport_work ();
129 void
130 Session::summon_butler ()
132 char c = ButlerRequest::Run;
133 ::write (butler_request_pipe[1], &c, 1);
134 // PBD::stacktrace (cerr);
137 void
138 Session::stop_butler ()
140 Glib::Mutex::Lock lm (butler_request_lock);
141 char c = ButlerRequest::Pause;
142 ::write (butler_request_pipe[1], &c, 1);
143 butler_paused.wait(butler_request_lock);
146 void
147 Session::wait_till_butler_finished ()
149 Glib::Mutex::Lock lm (butler_request_lock);
150 char c = ButlerRequest::Wake;
151 ::write (butler_request_pipe[1], &c, 1);
152 butler_paused.wait(butler_request_lock);
155 void *
156 Session::_butler_thread_work (void* arg)
158 PBD::notify_gui_about_thread_creation (pthread_self(), X_("Butler"));
159 return ((Session *) arg)->butler_thread_work ();
160 return 0;
163 void *
164 Session::butler_thread_work ()
166 uint32_t err = 0;
167 int32_t bytes;
168 bool compute_io;
169 microseconds_t begin, end;
171 struct pollfd pfd[1];
172 bool disk_work_outstanding = false;
173 DiskstreamList::iterator i;
175 while (true) {
176 pfd[0].fd = butler_request_pipe[0];
177 pfd[0].events = POLLIN|POLLERR|POLLHUP;
179 cerr << "Butler sleeps with DWO = " << disk_work_outstanding << endl;
181 if (poll (pfd, 1, (disk_work_outstanding ? 0 : -1)) < 0) {
183 if (errno == EINTR) {
184 continue;
187 error << string_compose (_("poll on butler request pipe failed (%1)"),
188 strerror (errno))
189 << endmsg;
190 break;
193 cerr << "Butler awake\n";
195 if (pfd[0].revents & ~POLLIN) {
196 error << string_compose (_("Error on butler thread request pipe: fd=%1 err=%2"), pfd[0].fd, pfd[0].revents) << endmsg;
197 cerr << string_compose (_("Error on butler thread request pipe: fd=%1 err=%2"), pfd[0].fd, pfd[0].revents) << endl;
198 break;
201 if (pfd[0].revents & POLLIN) {
203 char req;
205 /* empty the pipe of all current requests */
207 while (1) {
208 size_t nread = ::read (butler_request_pipe[0], &req, sizeof (req));
209 if (nread == 1) {
211 switch ((ButlerRequest::Type) req) {
213 case ButlerRequest::Wake:
214 cerr << "\tWAKE request\n";
215 break;
217 case ButlerRequest::Run:
218 cerr << "\tRUN request\n";
219 butler_should_run = true;
220 break;
222 case ButlerRequest::Pause:
223 cerr << "\tPAUSE request\n";
224 butler_should_run = false;
225 break;
227 case ButlerRequest::Quit:
228 cerr << "\tQUIT request\n";
229 pthread_exit_pbd (0);
230 /*NOTREACHED*/
231 break;
233 default:
234 break;
237 } else if (nread == 0) {
238 break;
239 } else if (errno == EAGAIN) {
240 break;
241 } else {
242 fatal << _("Error reading from butler request pipe") << endmsg;
243 /*NOTREACHED*/
248 disk_work_outstanding = false;
249 bytes = 0;
250 compute_io = true;
252 restart:
253 if (transport_work_requested()) {
254 cerr << "Do transport work\n";
255 butler_transport_work ();
256 cerr << "\tdone with that\n";
257 } else {
258 cerr << "no transport work to do\n";
261 begin = get_microseconds();
263 boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader ();
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 // }
269 for (i = dsl->begin(); !transport_work_requested() && butler_should_run && i != dsl->end(); ++i) {
271 boost::shared_ptr<Diskstream> ds = *i;
273 /* don't read inactive tracks */
275 IO* io = ds->io();
277 if (io && !io->active()) {
278 continue;
281 switch (ds->do_refill ()) {
282 case 0:
283 bytes += ds->read_data_count();
284 break;
285 case 1:
286 bytes += ds->read_data_count();
287 cerr << (*i)->name() << " not completely written, DWO = true\n";
288 disk_work_outstanding = true;
289 break;
291 default:
292 compute_io = false;
293 error << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << endmsg;
294 break;
299 if (i != dsl->begin() && i != dsl->end()) {
300 /* we didn't get to all the streams */
301 cerr << "Some streams not serviced, DWO = true\n";
302 disk_work_outstanding = true;
305 if (!err && transport_work_requested()) {
306 cerr << "transport worked is now requested, going back to the start\n";
307 goto restart;
310 if (compute_io) {
311 end = get_microseconds();
312 if(end-begin > 0) {
313 _read_data_rate = (float) bytes / (float) (end - begin);
314 } else { _read_data_rate = 0; // infinity better
318 bytes = 0;
319 compute_io = true;
320 begin = get_microseconds();
322 for (i = dsl->begin(); !transport_work_requested() && butler_should_run && i != dsl->end(); ++i) {
323 // cerr << "write behind for " << (*i)->name () << endl;
325 /* note that we still try to flush diskstreams attached to inactive routes
328 switch ((*i)->do_flush (Session::ButlerContext)) {
329 case 0:
330 bytes += (*i)->write_data_count();
331 break;
332 case 1:
333 bytes += (*i)->write_data_count();
334 cerr << (*i)->name() << " not completely written, DWO = true\n";
335 disk_work_outstanding = true;
336 break;
338 default:
339 err++;
340 compute_io = false;
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.
348 if (err && actively_recording()) {
349 /* stop the transport and try to catch as much possible
350 captured state as we can.
352 request_stop ();
355 if (i != dsl->begin() && i != dsl->end()) {
356 /* we didn't get to all the streams */
357 disk_work_outstanding = true;
360 if (!err && transport_work_requested()) {
361 cerr << "transport worked is now requested, going back to the start 2\n";
362 goto restart;
365 if (compute_io) {
366 // there are no apparent users for this calculation?
367 end = get_microseconds();
368 if(end-begin > 0) {
369 _write_data_rate = (float) bytes / (float) (end - begin);
370 } else {
371 _write_data_rate = 0; // Well, infinity would be better
375 if (!disk_work_outstanding) {
376 refresh_disk_space ();
381 Glib::Mutex::Lock lm (butler_request_lock);
383 if (butler_should_run && (disk_work_outstanding || transport_work_requested())) {
384 // for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
385 // cerr << "AFTER " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl;
386 // }
388 goto restart;
391 butler_paused.signal();
394 cerr << "Butler loop done\n";
397 pthread_exit_pbd (0);
398 /*NOTREACHED*/
399 return (0);
403 void
404 Session::request_overwrite_buffer (Diskstream* stream)
406 Event *ev = new Event (Event::Overwrite, Event::Add, Event::Immediate, 0, 0, 0.0);
407 ev->set_ptr (stream);
408 queue_event (ev);
411 /** Process thread. */
412 void
413 Session::overwrite_some_buffers (Diskstream* ds)
415 if (actively_recording()) {
416 return;
419 if (ds) {
421 ds->set_pending_overwrite (true);
423 } else {
425 boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
426 for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
427 (*i)->set_pending_overwrite (true);
431 post_transport_work = PostTransportWork (post_transport_work | PostTransportOverWrite);
432 schedule_butler_transport_work ();
435 float
436 Session::read_data_rate () const
438 /* disk i/o in excess of 10000MB/sec indicate the buffer cache
439 in action. ignore it.
441 return _read_data_rate > 10485.7600000f ? 0.0f : _read_data_rate;
444 float
445 Session::write_data_rate () const
447 /* disk i/o in excess of 10000MB/sec indicate the buffer cache
448 in action. ignore it.
450 return _write_data_rate > 10485.7600000f ? 0.0f : _write_data_rate;
453 uint32_t
454 Session::playback_load ()
456 return (uint32_t) g_atomic_int_get (&_playback_load);
459 uint32_t
460 Session::capture_load ()
462 return (uint32_t) g_atomic_int_get (&_capture_load);
465 uint32_t
466 Session::playback_load_min ()
468 return (uint32_t) g_atomic_int_get (&_playback_load_min);
471 uint32_t
472 Session::capture_load_min ()
474 return (uint32_t) g_atomic_int_get (&_capture_load_min);
477 void
478 Session::reset_capture_load_min ()
480 g_atomic_int_set (&_capture_load_min, 100);
484 void
485 Session::reset_playback_load_min ()
487 g_atomic_int_set (&_playback_load_min, 100);