PBD::ProcessSemaphore _execution_sem;
+ /** Signalled to start a run of the graph for a process callback */
PBD::ProcessSemaphore _callback_start_sem;
PBD::ProcessSemaphore _callback_done_sem;
PBD::ProcessSemaphore _cleanup_sem;
+ /** The number of processing threads that are asleep */
volatile gint _execution_tokens;
+ /** The number of unprocessed nodes that do not feed any other node; updated during processing */
volatile gint _finished_refcount;
+ /** The initial number of nodes that do not feed any other node (for each chain) */
volatile gint _init_finished_refcount[2];
bool _graph_empty;
bool _process_silent;
bool _process_noroll;
- int _process_retval;
+ int _process_retval;
bool _process_need_butler;
};
}
}
+/** Set up threads for running the graph */
void
Graph::reset_thread_list ()
{
}
_finished_refcount = _init_finished_refcount[chain];
+ /* Trigger the initial nodes for processing, which are the ones at the `input' end */
for (i=_init_trigger_list[chain].begin(); i!=_init_trigger_list[chain].end(); i++) {
this->trigger( i->get() );
}
pthread_mutex_unlock (&_trigger_mutex);
}
+/** Called when a node at the `output' end of the chain (ie one that has no-one to feed)
+ * is finished.
+ */
void
Graph::dec_ref()
{
again:
_callback_done_sem.signal ();
- // block until we are triggered.
+ /* Block until the a process callback triggers us */
_callback_start_sem.wait();
if (_quit_threads) {
dump(chain);
}
+/** Called by both the main thread and all helpers.
+ * @return true to quit, false to carry on.
+ */
bool
Graph::run_one()
{
to_run = 0;
}
+ /* the number of threads that are asleep */
int et = _execution_tokens;
+ /* the number of nodes that need to be run */
int ts = _trigger_queue.size();
+ /* hence how many threads to wake up */
int wakeup = min (et, ts);
+ /* 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));
pt->drop_buffers();
}
+/** Here's the main graph thread */
void
Graph::main_thread()
{
again:
_callback_start_sem.wait ();
+
DEBUG_TRACE(DEBUG::ProcessThreads, "main thread is awake\n");
if (_quit_threads) {
if (_graph_empty && !_quit_threads) {
_callback_done_sem.signal ();
- DEBUG_TRACE(DEBUG::ProcessThreads, "main thread sees graph done, goes back to slee\n");
+ DEBUG_TRACE(DEBUG::ProcessThreads, "main thread sees graph done, goes back to sleep\n");
goto again;
}
+ /* This loop will run forever */
while (1) {
DEBUG_TRACE(DEBUG::ProcessThreads, "main thread runs one graph node\n");
if (run_one()) {
void
GraphNode::prep (int chain)
{
+ /* This is the number of nodes that directly feed us */
_refcount = _init_refcount[chain];
}
+/** Called by another node to tell us that one of the nodes that feed us
+ * has been processed.
+ */
void
GraphNode::dec_ref()
{
- if (g_atomic_int_dec_and_test (&_refcount))
+ if (g_atomic_int_dec_and_test (&_refcount)) {
+ /* All the nodes that feed us are done, so we can queue this node
+ for processing.
+ */
_graph->trigger (this);
+ }
}
void
node_set_t::iterator i;
bool feeds_somebody = false;
+ /* Tell the nodes that we feed that we've finished */
for (i=_activation_set[chain].begin(); i!=_activation_set[chain].end(); i++) {
(*i)->dec_ref();
feeds_somebody = true;