Add method to graphviz plot the process-graph
[ardour.git] / libs / ardour / graph.cc
1 /*
2  * Copyright (C) 2010-2012 Carl Hetherington <carl@carlh.net>
3  * Copyright (C) 2010-2017 Paul Davis <paul@linuxaudiosystems.com>
4  * Copyright (C) 2013 John Emmas <john@creativepost.co.uk>
5  * Copyright (C) 2013 Tim Mayberry <mojofunk@gmail.com>
6  * Copyright (C) 2015-2019 Robin Gareus <robin@gareus.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 #include <cmath>
24 #include <stdio.h>
25
26 #include "pbd/compose.h"
27 #include "pbd/debug_rt_alloc.h"
28 #include "pbd/pthread_utils.h"
29
30 #include "ardour/audioengine.h"
31 #include "ardour/debug.h"
32 #include "ardour/graph.h"
33 #include "ardour/process_thread.h"
34 #include "ardour/route.h"
35 #include "ardour/session.h"
36 #include "ardour/types.h"
37
38 #include "pbd/i18n.h"
39
40 using namespace ARDOUR;
41 using namespace PBD;
42 using namespace std;
43
44 #ifdef DEBUG_RT_ALLOC
45 static Graph* graph = 0;
46
47 extern "C" {
48
49 int
50 alloc_allowed ()
51 {
52         return !graph->in_process_thread ();
53 }
54 }
55 #endif
56
57 #define g_atomic_uint_get(x) static_cast<guint> (g_atomic_int_get (x))
58
59 Graph::Graph (Session& session)
60         : SessionHandleRef (session)
61         , _execution_sem ("graph_execution", 0)
62         , _callback_start_sem ("graph_start", 0)
63         , _callback_done_sem ("graph_done", 0)
64         , _graph_empty (true)
65         , _current_chain (0)
66         , _pending_chain (0)
67         , _setup_chain (1)
68 {
69         g_atomic_int_set (&_terminal_refcnt, 0);
70         g_atomic_int_set (&_terminate, 0);
71         g_atomic_int_set (&_n_workers, 0);
72         g_atomic_int_set (&_idle_thread_cnt, 0);
73         g_atomic_int_set (&_trigger_queue_size, 0);
74
75         /* pre-allocate memory */
76         _trigger_queue.reserve (1024);
77
78         ARDOUR::AudioEngine::instance ()->Running.connect_same_thread (engine_connections, boost::bind (&Graph::reset_thread_list, this));
79         ARDOUR::AudioEngine::instance ()->Stopped.connect_same_thread (engine_connections, boost::bind (&Graph::engine_stopped, this));
80         ARDOUR::AudioEngine::instance ()->Halted.connect_same_thread (engine_connections, boost::bind (&Graph::engine_stopped, this));
81
82         reset_thread_list ();
83
84 #ifdef DEBUG_RT_ALLOC
85         graph             = this;
86         pbd_alloc_allowed = &::alloc_allowed;
87 #endif
88 }
89
90 void
91 Graph::engine_stopped ()
92 {
93 #ifndef NDEBUG
94         cerr << "Graph::engine_stopped. n_thread: " << AudioEngine::instance ()->process_thread_count () << endl;
95 #endif
96         if (AudioEngine::instance ()->process_thread_count () != 0) {
97                 drop_threads ();
98         }
99 }
100
101 /** Set up threads for running the graph */
102 void
103 Graph::reset_thread_list ()
104 {
105         uint32_t num_threads = how_many_dsp_threads ();
106         guint    n_workers   = g_atomic_uint_get (&_n_workers);
107
108         /* For now, we shouldn't be using the graph code if we only have 1 DSP thread */
109         assert (num_threads > 1);
110         assert (AudioEngine::instance ()->process_thread_count () == n_workers);
111
112         /* don't bother doing anything here if we already have the right
113          * number of threads.
114          */
115
116         if (AudioEngine::instance ()->process_thread_count () == num_threads) {
117                 return;
118         }
119
120         Glib::Threads::Mutex::Lock lm (_session.engine ().process_lock ());
121
122         if (n_workers > 0) {
123                 drop_threads ();
124         }
125
126         /* Allow threads to run */
127         g_atomic_int_set (&_terminate, 0);
128
129         if (AudioEngine::instance ()->create_process_thread (boost::bind (&Graph::main_thread, this)) != 0) {
130                 throw failed_constructor ();
131         }
132
133         for (uint32_t i = 1; i < num_threads; ++i) {
134                 if (AudioEngine::instance ()->create_process_thread (boost::bind (&Graph::helper_thread, this))) {
135                         throw failed_constructor ();
136                 }
137         }
138
139         while (g_atomic_uint_get (&_n_workers) + 1 != num_threads) {
140                 sched_yield ();
141         }
142 }
143
144 void
145 Graph::session_going_away ()
146 {
147         drop_threads ();
148
149         // now drop all references on the nodes.
150         _nodes_rt[0].clear ();
151         _nodes_rt[1].clear ();
152         _init_trigger_list[0].clear ();
153         _init_trigger_list[1].clear ();
154         g_atomic_int_set (&_trigger_queue_size, 0);
155         _trigger_queue.clear ();
156 }
157
158 void
159 Graph::drop_threads ()
160 {
161         Glib::Threads::Mutex::Lock ls (_swap_mutex);
162
163         /* Flag threads to terminate */
164         g_atomic_int_set (&_terminate, 1);
165
166         /* Wake-up sleeping threads */
167         guint tc = g_atomic_uint_get (&_idle_thread_cnt);
168         assert (tc == g_atomic_uint_get (&_n_workers));
169         for (guint i = 0; i < tc; ++i) {
170                 _execution_sem.signal ();
171         }
172
173         /* and the main thread */
174         _callback_start_sem.signal ();
175
176         /* join process threads */
177         AudioEngine::instance ()->join_process_threads ();
178
179         g_atomic_int_set (&_n_workers, 0);
180         g_atomic_int_set (&_idle_thread_cnt, 0);
181
182         /* signal main process thread if it's waiting for an already terminated thread */
183         _callback_done_sem.signal ();
184
185         /* reset semaphores.
186          * This is somewhat ugly, yet if a thread is killed (e.g jackd terminates
187          * abnormally), some semaphores are still unlocked.
188          */
189 #ifndef NDEBUG
190         int d1 = _execution_sem.reset ();
191         int d2 = _callback_start_sem.reset ();
192         int d3 = _callback_done_sem.reset ();
193         cerr << "Graph::drop_threads() sema-counts: " << d1 << ", " << d2 << ", " << d3 << endl;
194 #else
195         _execution_sem.reset ();
196         _callback_start_sem.reset ();
197         _callback_done_sem.reset ();
198 #endif
199 }
200
201 /* special case route removal -- called from Session::remove_routes */
202 void
203 Graph::clear_other_chain ()
204 {
205         Glib::Threads::Mutex::Lock ls (_swap_mutex);
206
207         while (1) {
208                 if (_setup_chain != _pending_chain) {
209                         for (node_list_t::iterator ni = _nodes_rt[_setup_chain].begin (); ni != _nodes_rt[_setup_chain].end (); ++ni) {
210                                 (*ni)->_activation_set[_setup_chain].clear ();
211                         }
212
213                         _nodes_rt[_setup_chain].clear ();
214                         _init_trigger_list[_setup_chain].clear ();
215                         break;
216                 }
217                 /* setup chain == pending chain - we have
218                  * to wait till this is no longer true.
219                  */
220                 _cleanup_cond.wait (_swap_mutex);
221         }
222 }
223
224 void
225 Graph::prep ()
226 {
227         if (_swap_mutex.trylock ()) {
228                 /* swap mutex acquired */
229                 if (_current_chain != _pending_chain) {
230                         /* use new chain */
231                         _setup_chain   = _current_chain;
232                         _current_chain = _pending_chain;
233                         /* ensure that all nodes can be queued */
234                         _trigger_queue.reserve (_nodes_rt[_current_chain].size ());
235                         assert (g_atomic_uint_get (&_trigger_queue_size) == 0);
236                         _cleanup_cond.signal ();
237                 }
238                 _swap_mutex.unlock ();
239         }
240
241         _graph_empty = true;
242
243         int chain = _current_chain;
244
245         node_list_t::iterator i;
246         for (i = _nodes_rt[chain].begin (); i != _nodes_rt[chain].end (); ++i) {
247                 (*i)->prep (chain);
248                 _graph_empty = false;
249         }
250
251         assert (_graph_empty != (_n_terminal_nodes[chain] > 0));
252
253         g_atomic_int_set (&_terminal_refcnt, _n_terminal_nodes[chain]);
254
255         /* Trigger the initial nodes for processing, which are the ones at the `input' end */
256         for (i = _init_trigger_list[chain].begin (); i != _init_trigger_list[chain].end (); i++) {
257                 g_atomic_int_inc (&_trigger_queue_size);
258                 _trigger_queue.push_back (i->get ());
259         }
260 }
261
262 void
263 Graph::trigger (GraphNode* n)
264 {
265         g_atomic_int_inc (&_trigger_queue_size);
266         _trigger_queue.push_back (n);
267 }
268
269 /** Called when a node at the `output' end of the chain (ie one that has no-one to feed)
270  *  is finished.
271  */
272 void
273 Graph::reached_terminal_node ()
274 {
275         if (g_atomic_int_dec_and_test (&_terminal_refcnt)) {
276         again:
277
278                 /* We have run all the nodes that are at the `output' end of
279                  * the graph, so there is nothing more to do this time around.
280                  */
281                 assert (g_atomic_uint_get (&_trigger_queue_size) == 0);
282
283                 /* Notify caller */
284                 DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 cycle done.\n", pthread_name ()));
285
286                 _callback_done_sem.signal ();
287
288                 /* Ensure that all background threads are idle.
289                  * When freewheeling there may be an immediate restart:
290                  * If there are more threads than CPU cores, some worker-
291                  * threads may only be "on the way" to become idle.
292                  */
293                 guint n_workers = g_atomic_uint_get (&_n_workers);
294                 while (g_atomic_uint_get (&_idle_thread_cnt) != n_workers) {
295                         sched_yield ();
296                 }
297
298                 /* Block until the a process callback */
299                 _callback_start_sem.wait ();
300
301                 if (g_atomic_int_get (&_terminate)) {
302                         return;
303                 }
304
305                 DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 prepare new cycle.\n", pthread_name ()));
306
307                 /* Prepare next cycle:
308                  *  - Reset terminal reference count
309                  *  - queue initial nodes
310                  */
311                 prep ();
312
313                 if (_graph_empty && !g_atomic_int_get (&_terminate)) {
314                         goto again;
315                 }
316                 /* .. continue in worker-thread */
317         }
318 }
319
320 /** Rechain our stuff using a list of routes (which can be in any order) and
321  *  a directed graph of their interconnections, which is guaranteed to be
322  *  acyclic.
323  */
324 void
325 Graph::rechain (boost::shared_ptr<RouteList> routelist, GraphEdges const& edges)
326 {
327         Glib::Threads::Mutex::Lock ls (_swap_mutex);
328
329         int chain = _setup_chain;
330         DEBUG_TRACE (DEBUG::Graph, string_compose ("============== setup %1\n", chain));
331
332         /* This will become the number of nodes that do not feed any other node;
333          * once we have processed this number of those nodes, we have finished.
334          */
335         _n_terminal_nodes[chain] = 0;
336
337         /* This will become a list of nodes that are not fed by another node, ie
338          * those at the `input' end.
339          */
340         _init_trigger_list[chain].clear ();
341
342         _nodes_rt[chain].clear ();
343
344         /* Clear things out, and make _nodes_rt[chain] a copy of routelist */
345         for (RouteList::iterator ri = routelist->begin (); ri != routelist->end (); ri++) {
346                 (*ri)->_init_refcount[chain] = 0;
347                 (*ri)->_activation_set[chain].clear ();
348                 _nodes_rt[chain].push_back (*ri);
349         }
350
351         // now add refs for the connections.
352
353         for (node_list_t::iterator ni = _nodes_rt[chain].begin (); ni != _nodes_rt[chain].end (); ni++) {
354                 boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (*ni);
355
356                 /* The routes that are directly fed by r */
357                 set<GraphVertex> fed_from_r = edges.from (r);
358
359                 /* Hence whether r has an output */
360                 bool const has_output = !fed_from_r.empty ();
361
362                 /* Set up r's activation set */
363                 for (set<GraphVertex>::iterator i = fed_from_r.begin (); i != fed_from_r.end (); ++i) {
364                         r->_activation_set[chain].insert (*i);
365                 }
366
367                 /* r has an input if there are some incoming edges to r in the graph */
368                 bool const has_input = !edges.has_none_to (r);
369
370                 /* Increment the refcount of any route that we directly feed */
371                 for (node_set_t::iterator ai = r->_activation_set[chain].begin (); ai != r->_activation_set[chain].end (); ai++) {
372                         (*ai)->_init_refcount[chain] += 1;
373                 }
374
375                 if (!has_input) {
376                         /* no input, so this node needs to be triggered initially to get things going */
377                         _init_trigger_list[chain].push_back (*ni);
378                 }
379
380                 if (!has_output) {
381                         /* no output, so this is one of the nodes that we can count off to decide
382                          * if we've finished
383                          */
384                         _n_terminal_nodes[chain] += 1;
385                 }
386         }
387
388         _pending_chain = chain;
389         dump (chain);
390 }
391
392 /** Called by both the main thread and all helpers. */
393 void
394 Graph::run_one ()
395 {
396         GraphNode* to_run = NULL;
397
398         if (g_atomic_int_get (&_terminate)) {
399                 return;
400         }
401
402         if (_trigger_queue.pop_front (to_run)) {
403                 /* Wake up idle threads, but at most as many as there's
404                  * work in the trigger queue that can be processed by
405                  * other threads.
406                  * This thread as not yet decreased _trigger_queue_size.
407                  */
408                 guint idle_cnt   = g_atomic_uint_get (&_idle_thread_cnt);
409                 guint work_avail = g_atomic_uint_get (&_trigger_queue_size);
410                 guint wakeup     = std::min (idle_cnt + 1, work_avail);
411
412                 DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 signals %2 threads\n", pthread_name (), wakeup));
413                 for (guint i = 1; i < wakeup; ++i) {
414                         _execution_sem.signal ();
415                 }
416         }
417
418         while (!to_run) {
419                 /* Wait for work, fall asleep */
420                 g_atomic_int_inc (&_idle_thread_cnt);
421                 assert (g_atomic_uint_get (&_idle_thread_cnt) <= _n_workers);
422
423                 DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 goes to sleep\n", pthread_name ()));
424                 _execution_sem.wait ();
425
426                 if (g_atomic_int_get (&_terminate)) {
427                         return;
428                 }
429
430                 DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 is awake\n", pthread_name ()));
431
432                 g_atomic_int_dec_and_test (&_idle_thread_cnt);
433
434                 /* Try to find some work to do */
435                 _trigger_queue.pop_front (to_run);
436         }
437
438         /* Process the graph-node */
439         g_atomic_int_dec_and_test (&_trigger_queue_size);
440         to_run->run (_current_chain);
441
442         DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 has finished run_one()\n", pthread_name ()));
443 }
444
445 void
446 Graph::helper_thread ()
447 {
448         g_atomic_int_inc (&_n_workers);
449         guint id = g_atomic_uint_get (&_n_workers);
450
451         /* This is needed for ARDOUR::Session requests called from rt-processors
452          * in particular Lua scripts may do cross-thread calls */
453         if (!SessionEvent::has_per_thread_pool ()) {
454                 char name[64];
455                 snprintf (name, 64, "RT-%u-%p", id, (void*)DEBUG_THREAD_SELF);
456                 pthread_set_name (name);
457                 SessionEvent::create_per_thread_pool (name, 64);
458                 PBD::notify_event_loops_about_thread_creation (pthread_self (), name, 64);
459         }
460
461         suspend_rt_malloc_checks ();
462         ProcessThread* pt = new ProcessThread ();
463         resume_rt_malloc_checks ();
464
465         pt->get_buffers ();
466
467         while (!g_atomic_int_get (&_terminate)) {
468                 run_one ();
469         }
470
471         pt->drop_buffers ();
472         delete pt;
473 }
474
475 /** Here's the main graph thread */
476 void
477 Graph::main_thread ()
478 {
479         /* first time setup */
480
481         suspend_rt_malloc_checks ();
482         ProcessThread* pt = new ProcessThread ();
483
484         /* This is needed for ARDOUR::Session requests called from rt-processors
485          * in particular Lua scripts may do cross-thread calls */
486         if (!SessionEvent::has_per_thread_pool ()) {
487                 char name[64];
488                 snprintf (name, 64, "RT-main-%p", (void*)DEBUG_THREAD_SELF);
489                 pthread_set_name (name);
490                 SessionEvent::create_per_thread_pool (name, 64);
491                 PBD::notify_event_loops_about_thread_creation (pthread_self (), name, 64);
492         }
493         resume_rt_malloc_checks ();
494
495         pt->get_buffers ();
496
497         /* Wait for initial process callback */
498 again:
499         _callback_start_sem.wait ();
500
501         DEBUG_TRACE (DEBUG::ProcessThreads, "main thread is awake\n");
502
503         if (g_atomic_int_get (&_terminate)) {
504                 pt->drop_buffers ();
505                 delete (pt);
506                 return;
507         }
508
509         /* Bootstrap the trigger-list
510          * (later this is done by Graph_reached_terminal_node) */
511         prep ();
512
513         if (_graph_empty && !g_atomic_int_get (&_terminate)) {
514                 _callback_done_sem.signal ();
515                 DEBUG_TRACE (DEBUG::ProcessThreads, "main thread sees graph done, goes back to sleep\n");
516                 goto again;
517         }
518
519         /* After setup, the main-thread just becomes a normal worker */
520         while (!g_atomic_int_get (&_terminate)) {
521                 run_one ();
522         }
523
524         pt->drop_buffers ();
525         delete (pt);
526 }
527
528 void
529 Graph::dump (int chain) const
530 {
531 #ifndef NDEBUG
532         node_list_t::const_iterator ni;
533         node_set_t::const_iterator  ai;
534
535         chain = _pending_chain;
536
537         DEBUG_TRACE (DEBUG::Graph, "--------------------------------------------Graph dump:\n");
538         for (ni = _nodes_rt[chain].begin (); ni != _nodes_rt[chain].end (); ni++) {
539                 boost::shared_ptr<Route> rp = boost::dynamic_pointer_cast<Route> (*ni);
540                 DEBUG_TRACE (DEBUG::Graph, string_compose ("GraphNode: %1  refcount: %2\n", rp->name ().c_str (), (*ni)->_init_refcount[chain]));
541                 for (ai = (*ni)->_activation_set[chain].begin (); ai != (*ni)->_activation_set[chain].end (); ai++) {
542                         DEBUG_TRACE (DEBUG::Graph, string_compose ("  triggers: %1\n", boost::dynamic_pointer_cast<Route> (*ai)->name ().c_str ()));
543                 }
544         }
545
546         DEBUG_TRACE (DEBUG::Graph, "------------- trigger list:\n");
547         for (ni = _init_trigger_list[chain].begin (); ni != _init_trigger_list[chain].end (); ni++) {
548                 DEBUG_TRACE (DEBUG::Graph, string_compose ("GraphNode: %1  refcount: %2\n", boost::dynamic_pointer_cast<Route> (*ni)->name ().c_str (), (*ni)->_init_refcount[chain]));
549         }
550
551         DEBUG_TRACE (DEBUG::Graph, string_compose ("final activation refcount: %1\n", _n_terminal_nodes[chain]));
552 #endif
553 }
554
555 bool
556 Graph::plot (std::string const& file_name) const
557 {
558         Glib::Threads::Mutex::Lock ls (_swap_mutex);
559         int chain = _current_chain;
560
561         node_list_t::const_iterator ni;
562         node_set_t::const_iterator  ai;
563         stringstream ss;
564
565         ss << "digraph {\n";
566         ss << "  node [shape = ellipse];\n";
567
568         for (ni = _nodes_rt[chain].begin (); ni != _nodes_rt[chain].end (); ni++) {
569                 boost::shared_ptr<Route> sr = boost::dynamic_pointer_cast<Route> (*ni);
570                 std::string sn = string_compose ("%1 (%2)", sr->name (), (*ni)->_init_refcount[chain]);
571                 if ((*ni)->_init_refcount[chain] == 0 && (*ni)->_activation_set[chain].size() == 0) {
572                                 ss << "  \"" << sn << "\"[style=filled,fillcolor=gold1];\n";
573                 } else if ((*ni)->_init_refcount[chain] == 0) {
574                                 ss << "  \"" << sn << "\"[style=filled,fillcolor=lightskyblue1];\n";
575                 } else if ((*ni)->_activation_set[chain].size() == 0) {
576                                 ss << "  \"" << sn << "\"[style=filled,fillcolor=aquamarine2];\n";
577                 }
578                 for (ai = (*ni)->_activation_set[chain].begin (); ai != (*ni)->_activation_set[chain].end (); ai++) {
579                         boost::shared_ptr<Route> dr = boost::dynamic_pointer_cast<Route> (*ai);
580                         std::string dn = string_compose ("%1 (%2)", dr->name (), (*ai)->_init_refcount[chain]);
581                         bool sends_only = false;
582                         sr->feeds (dr, &sends_only);
583                         if (sends_only) {
584                                 ss << "  edge [style=dashed];\n";
585                         }
586                         ss << "  \"" << sn << "\" -> \"" << dn << "\"\n";
587                         if (sends_only) {
588                                 ss << "  edge [style=solid];\n";
589                         }
590                 }
591         }
592         ss << "}\n";
593
594         GError *err = NULL;
595         if (!g_file_set_contents (file_name.c_str(), ss.str().c_str(), -1, &err)) {
596                 if (err) {
597                         error << string_compose (_("Could not graph to file (%1)"), err->message) << endmsg;
598                         g_error_free (err);
599                 }
600                 return false;
601         }
602         return true;
603 }
604
605 int
606 Graph::process_routes (pframes_t nframes, samplepos_t start_sample, samplepos_t end_sample, bool& need_butler)
607 {
608         DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("graph execution from %1 to %2 = %3\n", start_sample, end_sample, nframes));
609
610         if (g_atomic_int_get (&_terminate)) {
611                 return 0;
612         }
613
614         _process_nframes      = nframes;
615         _process_start_sample = start_sample;
616         _process_end_sample   = end_sample;
617
618         _process_noroll      = false;
619         _process_retval      = 0;
620         _process_need_butler = false;
621
622         DEBUG_TRACE (DEBUG::ProcessThreads, "wake graph for non-silent process\n");
623         _callback_start_sem.signal ();
624         _callback_done_sem.wait ();
625         DEBUG_TRACE (DEBUG::ProcessThreads, "graph execution complete\n");
626
627         need_butler = _process_need_butler;
628
629         return _process_retval;
630 }
631
632 int
633 Graph::routes_no_roll (pframes_t nframes, samplepos_t start_sample, samplepos_t end_sample, bool non_rt_pending)
634 {
635         DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("no-roll graph execution from %1 to %2 = %3\n", start_sample, end_sample, nframes));
636
637         if (g_atomic_int_get (&_terminate)) {
638                 return 0;
639         }
640
641         _process_nframes        = nframes;
642         _process_start_sample   = start_sample;
643         _process_end_sample     = end_sample;
644         _process_non_rt_pending = non_rt_pending;
645
646         _process_noroll      = true;
647         _process_retval      = 0;
648         _process_need_butler = false;
649
650         DEBUG_TRACE (DEBUG::ProcessThreads, "wake graph for no-roll process\n");
651         _callback_start_sem.signal ();
652         _callback_done_sem.wait ();
653         DEBUG_TRACE (DEBUG::ProcessThreads, "graph execution complete\n");
654
655         return _process_retval;
656 }
657 void
658 Graph::process_one_route (Route* route)
659 {
660         bool need_butler = false;
661         int  retval;
662
663         assert (route);
664
665         DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 runs route %2\n", pthread_name (), route->name ()));
666
667         if (_process_noroll) {
668                 retval = route->no_roll (_process_nframes, _process_start_sample, _process_end_sample, _process_non_rt_pending);
669         } else {
670                 retval = route->roll (_process_nframes, _process_start_sample, _process_end_sample, need_butler);
671         }
672
673         if (retval) {
674                 _process_retval = retval;
675         }
676
677         if (need_butler) {
678                 _process_need_butler = true;
679         }
680 }
681
682 bool
683 Graph::in_process_thread () const
684 {
685         return AudioEngine::instance ()->in_process_thread ();
686 }