'libs/ardour' - Main body of changes required for building with MSVC
[ardour.git] / libs / ardour / graph.cc
index aea4b2cccf1ebb64143e64b30446b741db729c56..fe76fd74edf0bbe3c439d272ee3dcd96479334a2 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "pbd/compose.h"
 #include "pbd/debug_rt_alloc.h"
+#include "pbd/pthread_utils.h"
 
 #include "ardour/debug.h"
 #include "ardour/graph.h"
@@ -77,28 +78,21 @@ Graph::Graph (Session & session)
 
         reset_thread_list ();
 
-        Config->ParameterChanged.connect_same_thread (processor_usage_connection, boost::bind (&Graph::parameter_changed, this, _1));
-
 #ifdef DEBUG_RT_ALLOC
        graph = this;
        pbd_alloc_allowed = &::alloc_allowed;
 #endif
 }
 
-void
-Graph::parameter_changed (std::string param)
-{
-        if (param == X_("processor-usage")) {
-                reset_thread_list ();
-        }
-}
-
 /** Set up threads for running the graph */
 void
 Graph::reset_thread_list ()
 {
         uint32_t num_threads = how_many_dsp_threads ();
 
+       /* For now, we shouldn't be using the graph code if we only have 1 DSP thread */
+       assert (num_threads > 1);
+
         /* don't bother doing anything here if we already have the right
            number of threads.
         */
@@ -107,31 +101,25 @@ Graph::reset_thread_list ()
                 return;
         }
 
-        Glib::Mutex::Lock lm (_session.engine().process_lock());
-       pthread_t a_thread;
+        Glib::Threads::Mutex::Lock lm (_session.engine().process_lock());
+       jack_native_thread_t a_thread;
 
         if (!_thread_list.empty()) {
                 drop_threads ();
         }
 
-#if 0
-        /* XXX this only makes sense when we can use just the AudioEngine thread
-           and still keep the graph current with the route list
-        */
-        if (num_threads <= 1) {
-                /* no point creating 1 thread - the AudioEngine already gives us one
-                 */
-                return;
-        }
-#endif
-       if (AudioEngine::instance()->create_process_thread (boost::bind (&Graph::main_thread, this), &a_thread, 100000) == 0) {
-               _thread_list.push_back (a_thread);
+       if (AudioEngine::instance()->create_process_thread (boost::bind (&Graph::main_thread, this), &a_thread, 100000) != 0) {
+               throw failed_constructor ();
        }
 
+       _thread_list.push_back (a_thread);
+
         for (uint32_t i = 1; i < num_threads; ++i) {
-               if (AudioEngine::instance()->create_process_thread (boost::bind (&Graph::helper_thread, this), &a_thread, 100000) == 0) {
-                       _thread_list.push_back (a_thread);
+               if (AudioEngine::instance()->create_process_thread (boost::bind (&Graph::helper_thread, this), &a_thread, 100000) != 0) {
+                       throw failed_constructor ();
                }
+               
+               _thread_list.push_back (a_thread);
         }
 }
 
@@ -159,9 +147,8 @@ Graph::drop_threads ()
 
         _callback_start_sem.signal ();
 
-        for (list<pthread_t>::iterator i = _thread_list.begin(); i != _thread_list.end(); ++i) {
-                void* status;
-                pthread_join (*i, &status);
+        for (list<jack_native_thread_t>::iterator i = _thread_list.begin(); i != _thread_list.end(); ++i) {
+                AudioEngine::instance()->stop_process_thread(*i);
         }
 
         _thread_list.clear ();
@@ -174,7 +161,7 @@ Graph::drop_threads ()
 void
 Graph::clear_other_chain ()
 {
-        Glib::Mutex::Lock ls (_swap_mutex);
+        Glib::Threads::Mutex::Lock ls (_swap_mutex);
 
         while (1) {
                 if (_setup_chain != _pending_chain) {
@@ -222,17 +209,20 @@ Graph::prep()
         _finished_refcount = _init_finished_refcount[chain];
 
        /* Trigger the initial nodes for processing, which are the ones at the `input' end */
+       pthread_mutex_lock (&_trigger_mutex);
         for (i=_init_trigger_list[chain].begin(); i!=_init_trigger_list[chain].end(); i++) {
-                trigger (i->get ());
+               /* don't use ::trigger here, as we have already locked the mutex */
+                _trigger_queue.push_back (i->get ());
         }
+       pthread_mutex_unlock (&_trigger_mutex);
 }
 
 void
 Graph::trigger (GraphNode* n)
 {
-        pthread_mutex_lock (&_trigger_mutex);
+       pthread_mutex_lock (&_trigger_mutex);
         _trigger_queue.push_back (n);
-        pthread_mutex_unlock (&_trigger_mutex);
+       pthread_mutex_unlock (&_trigger_mutex);
 }
 
 /** Called when a node at the `output' end of the chain (ie one that has no-one to feed)
@@ -276,45 +266,18 @@ Graph::restart_cycle()
         // starting with waking up the others.
 }
 
-static bool
-is_feedback (boost::shared_ptr<RouteList> routelist, Route* from, boost::shared_ptr<Route> to)
-{
-        for (RouteList::iterator ri=routelist->begin(); ri!=routelist->end(); ri++) {
-                if ((*ri).get() == from) {
-                        return false;
-               }
-                if ((*ri) == to) {
-                        return true;
-               }
-        }
-
-        return false;
-}
-
-static bool
-is_feedback (boost::shared_ptr<RouteList> routelist, boost::shared_ptr<Route> from, Route* to)
-{
-        for (RouteList::iterator ri=routelist->begin(); ri!=routelist->end(); ri++) {
-                if ((*ri).get() == to) {
-                        return true;
-               }
-                if ((*ri) == from) {
-                        return false;
-               }
-        }
-
-        return false;
-}
+/** Rechain our stuff using a list of routes (which can be in any order) and
+ *  a directed graph of their interconnections, which is guaranteed to be
+ *  acyclic.
+ */
 
 void
-Graph::rechain (boost::shared_ptr<RouteList> routelist)
+Graph::rechain (boost::shared_ptr<RouteList> routelist, GraphEdges const & edges)
 {
-        node_list_t::iterator ni;
-        Glib::Mutex::Lock ls (_swap_mutex);
+        Glib::Threads::Mutex::Lock ls (_swap_mutex);
 
         int chain = _setup_chain;
         DEBUG_TRACE (DEBUG::Graph, string_compose ("============== setup %1\n", chain));
-        // set all refcounts to 0;
 
        /* This will become the number of nodes that do not feed any other node;
           once we have processed this number of those nodes, we have finished.
@@ -330,62 +293,45 @@ Graph::rechain (boost::shared_ptr<RouteList> routelist)
 
        /* Clear things out, and make _nodes_rt[chain] a copy of routelist */
         for (RouteList::iterator ri=routelist->begin(); ri!=routelist->end(); ri++) {
-                node_ptr_t n = boost::dynamic_pointer_cast<GraphNode> (*ri);
-
-                n->_init_refcount[chain] = 0;
-                n->_activation_set[chain].clear();
-                _nodes_rt[chain].push_back(n);
+                (*ri)->_init_refcount[chain] = 0;
+                (*ri)->_activation_set[chain].clear();
+                _nodes_rt[chain].push_back (*ri);
         }
 
         // now add refs for the connections.
 
-        for (ni=_nodes_rt[chain].begin(); ni!=_nodes_rt[chain].end(); ni++) {
-
-               /* We will set this to true if the node *ni is directly or
-                  indirectly fed by anything (without feedback)
-               */
-                bool has_input  = false;
-
-               /* We will set this to true if the node *ni directly feeds
-                  anything (without feedback)
-               */
-                bool has_output = false;
+        for (node_list_t::iterator ni = _nodes_rt[chain].begin(); ni != _nodes_rt[chain].end(); ni++) {
 
-               /* We will also set up *ni's _activation_set to contain any nodes
-                  that it directly feeds.
-               */
+                boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (*ni);
 
-                boost::shared_ptr<Route> rp = boost::dynamic_pointer_cast<Route>( *ni);
+               /* The routes that are directly fed by r */
+               set<GraphVertex> fed_from_r = edges.from (r);
 
-                for (RouteList::iterator ri=routelist->begin(); ri!=routelist->end(); ri++) {
-                        if (rp->direct_feeds (*ri)) {
-                                if (is_feedback (routelist, rp.get(), *ri)) {
-                                        continue;
-                                }
+               /* Hence whether r has an output */
+               bool const has_output = !fed_from_r.empty ();
 
-                                has_output = true;
-                                (*ni)->_activation_set[chain].insert (boost::dynamic_pointer_cast<GraphNode> (*ri) );
-                        }
-                }
+               /* Set up r's activation set */
+               for (set<GraphVertex>::iterator i = fed_from_r.begin(); i != fed_from_r.end(); ++i) {
+                       r->_activation_set[chain].insert (*i);
+               }
 
-                for (Route::FedBy::iterator fi=rp->fed_by().begin(); fi!=rp->fed_by().end(); fi++) {
-                        if (boost::shared_ptr<Route> r = fi->r.lock()) {
-                                if (!is_feedback (routelist, r, rp.get())) {
-                                        has_input = true;
-                                }
-                        }
-                }
+               /* r has an input if there are some incoming edges to r in the graph */
+               bool const has_input = !edges.has_none_to (r);
 
                /* Increment the refcount of any route that we directly feed */
-                for (node_set_t::iterator ai=(*ni)->_activation_set[chain].begin(); ai!=(*ni)->_activation_set[chain].end(); ai++) {
+                for (node_set_t::iterator ai = r->_activation_set[chain].begin(); ai != r->_activation_set[chain].end(); ai++) {
                         (*ai)->_init_refcount[chain] += 1;
                 }
 
                 if (!has_input) {
+                       /* no input, so this node needs to be triggered initially to get things going */
                         _init_trigger_list[chain].push_back (*ni);
                }
 
                 if (!has_output) {
+                       /* no output, so this is one of the nodes that we can count off to decide
+                          if we've finished
+                       */
                         _init_finished_refcount[chain] += 1;
                }
         }
@@ -420,7 +366,7 @@ Graph::run_one()
        /* update the number of threads that will still be sleeping */
         _execution_tokens -= wakeup;
 
-        DEBUG_TRACE(DEBUG::ProcessThreads, string_compose ("%1 signals %2\n", pthread_self(), wakeup));
+        DEBUG_TRACE(DEBUG::ProcessThreads, string_compose ("%1 signals %2\n", pthread_name(), wakeup));
 
         for (int i = 0; i < wakeup; i++) {
                 _execution_sem.signal ();
@@ -429,12 +375,12 @@ Graph::run_one()
         while (to_run == 0) {
                 _execution_tokens += 1;
                 pthread_mutex_unlock (&_trigger_mutex);
-                DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 goes to sleep\n", pthread_self()));
+                DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 goes to sleep\n", pthread_name()));
                 _execution_sem.wait ();
                 if (_quit_threads) {
                         return true;
                 }
-                DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 is awake\n", pthread_self()));
+                DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 is awake\n", pthread_name()));
                 pthread_mutex_lock (&_trigger_mutex);
                 if (_trigger_queue.size()) {
                         to_run = _trigger_queue.back();
@@ -446,29 +392,11 @@ Graph::run_one()
         to_run->process();
         to_run->finish (_current_chain);
 
-        DEBUG_TRACE(DEBUG::ProcessThreads, string_compose ("%1 has finished run_one()\n", pthread_self()));
+        DEBUG_TRACE(DEBUG::ProcessThreads, string_compose ("%1 has finished run_one()\n", pthread_name()));
 
         return false;
 }
 
-static void get_rt()
-{
-        if (!jack_is_realtime (AudioEngine::instance()->jack())) {
-                return;
-        }
-
-        int priority = jack_client_real_time_priority (AudioEngine::instance()->jack());
-
-        if (priority) {
-                struct sched_param rtparam;
-
-                memset (&rtparam, 0, sizeof (rtparam));
-                rtparam.sched_priority = priority;
-
-                pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam);
-        }
-}
-
 void
 Graph::helper_thread()
 {
@@ -477,7 +405,6 @@ Graph::helper_thread()
        resume_rt_malloc_checks ();
 
         pt->get_buffers();
-        get_rt();
 
         while(1) {
                 if (run_one()) {
@@ -497,7 +424,6 @@ Graph::main_thread()
        resume_rt_malloc_checks ();
 
         pt->get_buffers();
-        get_rt();
 
   again:
         _callback_start_sem.wait ();
@@ -634,7 +560,7 @@ Graph::process_one_route (Route* route)
 
         assert (route);
 
-        DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 runs route %2\n", pthread_self(), route->name()));
+        DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 runs route %2\n", pthread_name(), route->name()));
 
         if (_process_silent) {
                 retval = route->silent_roll (_process_nframes, _process_start_frame, _process_end_frame, need_butler);
@@ -658,10 +584,10 @@ Graph::process_one_route (Route* route)
 bool
 Graph::in_process_thread () const
 {
-       list<pthread_t>::const_iterator i = _thread_list.begin ();
-       while (i != _thread_list.end() && *i != pthread_self ()) {
-               ++i;
+       for (list<pthread_t>::const_iterator i = _thread_list.begin (); i != _thread_list.end(); ++i) {
+               if (pthread_equal(*i, pthread_self())) {
+                       return true;
+               }
        }
-
-       return i != _thread_list.end ();
+       return false;
 }