and yet more of a possible fix for NPAE problem
[ardour2.git] / libs / ardour / session_butler.cc
blob17827beed71038a291ec99eafe5297fe48dae1b0
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 bytes = 0;
249 compute_io = true;
251 restart:
253 disk_work_outstanding = false;
254 cerr << "transport work = " << g_atomic_int_get (&butler_should_do_transport_work) << endl;
255 if (transport_work_requested()) {
256 cerr << "Do transport work\n";
257 butler_transport_work ();
258 cerr << "\tdone with that\n";
259 } else {
260 cerr << "no transport work to do\n";
263 begin = get_microseconds();
265 boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader ();
267 // for (i = dsl->begin(); i != dsl->end(); ++i) {
268 // cerr << "BEFORE " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl;
269 // }
271 for (i = dsl->begin(); !transport_work_requested() && butler_should_run && i != dsl->end(); ++i) {
273 boost::shared_ptr<Diskstream> ds = *i;
275 /* don't read inactive tracks */
277 IO* io = ds->io();
279 if (io && !io->active()) {
280 continue;
283 switch (ds->do_refill ()) {
284 case 0:
285 bytes += ds->read_data_count();
286 break;
287 case 1:
288 bytes += ds->read_data_count();
289 cerr << (*i)->name() << " not completely written, DWO = true\n";
290 disk_work_outstanding = true;
291 break;
293 default:
294 compute_io = false;
295 error << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << endmsg;
296 break;
301 if (i != dsl->begin() && i != dsl->end()) {
302 /* we didn't get to all the streams */
303 cerr << "Some streams not serviced, DWO = true\n";
304 disk_work_outstanding = true;
307 if (!err && transport_work_requested()) {
308 cerr << "transport worked is now requested, going back to the start\n";
309 goto restart;
312 if (compute_io) {
313 end = get_microseconds();
314 if(end-begin > 0) {
315 _read_data_rate = (float) bytes / (float) (end - begin);
316 } else { _read_data_rate = 0; // infinity better
320 bytes = 0;
321 compute_io = true;
322 begin = get_microseconds();
324 for (i = dsl->begin(); !transport_work_requested() && butler_should_run && i != dsl->end(); ++i) {
325 // cerr << "write behind for " << (*i)->name () << endl;
327 /* note that we still try to flush diskstreams attached to inactive routes
330 switch ((*i)->do_flush (Session::ButlerContext)) {
331 case 0:
332 bytes += (*i)->write_data_count();
333 break;
334 case 1:
335 bytes += (*i)->write_data_count();
336 cerr << (*i)->name() << " not completely written, DWO = true\n";
337 disk_work_outstanding = true;
338 break;
340 default:
341 err++;
342 compute_io = false;
343 error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
344 /* don't break - try to flush all streams in case they
345 are split across disks.
350 if (err && actively_recording()) {
351 /* stop the transport and try to catch as much possible
352 captured state as we can.
354 request_stop ();
357 if (i != dsl->begin() && i != dsl->end()) {
358 /* we didn't get to all the streams */
359 disk_work_outstanding = true;
362 if (!err && transport_work_requested()) {
363 cerr << "transport worked is now requested, going back to the start 2\n";
364 goto restart;
367 if (compute_io) {
368 // there are no apparent users for this calculation?
369 end = get_microseconds();
370 if(end-begin > 0) {
371 _write_data_rate = (float) bytes / (float) (end - begin);
372 } else {
373 _write_data_rate = 0; // Well, infinity would be better
377 if (!disk_work_outstanding) {
378 refresh_disk_space ();
383 Glib::Mutex::Lock lm (butler_request_lock);
385 if (butler_should_run && (disk_work_outstanding || transport_work_requested())) {
386 // for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
387 // cerr << "AFTER " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl;
388 // }
390 cerr << "Loop done, but BSR = "
391 << butler_should_run
392 << " DWO = " << disk_work_outstanding
393 << " TWR = " << g_atomic_int_get (&butler_should_do_transport_work)
394 << " so back to restart\n";
395 goto restart;
398 butler_paused.signal();
401 cerr << "Butler loop done\n";
404 pthread_exit_pbd (0);
405 /*NOTREACHED*/
406 return (0);
410 void
411 Session::request_overwrite_buffer (Diskstream* stream)
413 Event *ev = new Event (Event::Overwrite, Event::Add, Event::Immediate, 0, 0, 0.0);
414 ev->set_ptr (stream);
415 queue_event (ev);
418 /** Process thread. */
419 void
420 Session::overwrite_some_buffers (Diskstream* ds)
422 if (actively_recording()) {
423 return;
426 if (ds) {
428 ds->set_pending_overwrite (true);
430 } else {
432 boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
433 for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
434 (*i)->set_pending_overwrite (true);
438 post_transport_work = PostTransportWork (post_transport_work | PostTransportOverWrite);
439 schedule_butler_transport_work ();
442 float
443 Session::read_data_rate () const
445 /* disk i/o in excess of 10000MB/sec indicate the buffer cache
446 in action. ignore it.
448 return _read_data_rate > 10485.7600000f ? 0.0f : _read_data_rate;
451 float
452 Session::write_data_rate () const
454 /* disk i/o in excess of 10000MB/sec indicate the buffer cache
455 in action. ignore it.
457 return _write_data_rate > 10485.7600000f ? 0.0f : _write_data_rate;
460 uint32_t
461 Session::playback_load ()
463 return (uint32_t) g_atomic_int_get (&_playback_load);
466 uint32_t
467 Session::capture_load ()
469 return (uint32_t) g_atomic_int_get (&_capture_load);
472 uint32_t
473 Session::playback_load_min ()
475 return (uint32_t) g_atomic_int_get (&_playback_load_min);
478 uint32_t
479 Session::capture_load_min ()
481 return (uint32_t) g_atomic_int_get (&_capture_load_min);
484 void
485 Session::reset_capture_load_min ()
487 g_atomic_int_set (&_capture_load_min, 100);
491 void
492 Session::reset_playback_load_min ()
494 g_atomic_int_set (&_playback_load_min, 100);