TaskBatchPtr batchTasksWalk( const TaskNode::Task &task, const std::set<const TaskBatch *> &ancestors = std::set<const TaskBatch *>() ) { // Acquire a batch with this task placed in it, // and check that we haven't discovered a cyclic // dependency. TaskBatchPtr batch = acquireBatch( task ); if( ancestors.find( batch.get() ) != ancestors.end() ) { throw IECore::Exception( ( boost::format( "Dispatched tasks cannot have cyclic dependencies but %s is involved in a cycle." ) % batch->plug()->relativeName( batch->plug()->ancestor<ScriptNode>() ) ).str() ); } // Ask the task what preTasks and postTasks it would like. TaskNode::Tasks preTasks; TaskNode::Tasks postTasks; { Context::Scope scopedTaskContext( task.context() ); task.plug()->preTasks( preTasks ); task.plug()->postTasks( postTasks ); } // Collect all the batches the postTasks belong in. // We grab these first because they need to be included // in the ancestors for cycle detection when getting // the preTask batches. TaskBatches postBatches; for( TaskNode::Tasks::const_iterator it = postTasks.begin(); it != postTasks.end(); ++it ) { postBatches.push_back( batchTasksWalk( *it ) ); } // Collect all the batches the preTasks belong in, // and add them as preTasks for our batch. std::set<const TaskBatch *> preTaskAncestors( ancestors ); preTaskAncestors.insert( batch.get() ); for( TaskBatches::const_iterator it = postBatches.begin(), eIt = postBatches.end(); it != eIt; ++it ) { preTaskAncestors.insert( it->get() ); } for( TaskNode::Tasks::const_iterator it = preTasks.begin(); it != preTasks.end(); ++it ) { addPreTask( batch.get(), batchTasksWalk( *it, preTaskAncestors ) ); } // As far as TaskBatch and doDispatch() are concerned, there // is no such thing as a postTask, so we emulate them by making // this batch a preTask of each of the postTask batches. We also // add the postTask batches as preTasks for the root, so that they // are reachable from doDispatch(). for( TaskBatches::const_iterator it = postBatches.begin(), eIt = postBatches.end(); it != eIt; ++it ) { addPreTask( it->get(), batch, /* forPostTask = */ true ); addPreTask( m_rootBatch.get(), *it ); } return batch; }
void Dispatcher::batchTasksWalk( Dispatcher::TaskBatchPtr parent, const ExecutableNode::Task &task, BatchMap ¤tBatches, TaskToBatchMap &tasksToBatches, std::set<const TaskBatch *> &ancestors ) { TaskBatchPtr batch = acquireBatch( task, currentBatches, tasksToBatches ); TaskBatches &parentRequirements = parent->requirements(); if ( std::find( parentRequirements.begin(), parentRequirements.end(), batch ) == parentRequirements.end() ) { if ( ancestors.find( batch.get() ) != ancestors.end() ) { throw IECore::Exception( ( boost::format( "Dispatched nodes cannot have cyclic dependencies. %s and %s are involved in a cycle." ) % batch->node()->relativeName( batch->node()->scriptNode() ) % parent->node()->relativeName( parent->node()->scriptNode() ) ).str() ); } parentRequirements.push_back( batch ); } ExecutableNode::Tasks taskRequirements; task.node()->requirements( task.context(), taskRequirements ); ancestors.insert( parent.get() ); for ( ExecutableNode::Tasks::const_iterator it = taskRequirements.begin(); it != taskRequirements.end(); ++it ) { batchTasksWalk( batch, *it, currentBatches, tasksToBatches, ancestors ); } ancestors.erase( parent.get() ); }
void Dispatcher::dispatch( const std::vector<NodePtr> &nodes ) const { // clear job directory, so that if our node validation fails, // jobDirectory() won't return the result from the previous dispatch. m_jobDirectory = ""; // validate the nodes we've been given if ( nodes.empty() ) { throw IECore::Exception( getName().string() + ": Must specify at least one node to dispatch." ); } std::vector<ExecutableNodePtr> executables; const ScriptNode *script = (*nodes.begin())->scriptNode(); for ( std::vector<NodePtr>::const_iterator nIt = nodes.begin(); nIt != nodes.end(); ++nIt ) { const ScriptNode *currentScript = (*nIt)->scriptNode(); if ( !currentScript || currentScript != script ) { throw IECore::Exception( getName().string() + ": Dispatched nodes must all belong to the same ScriptNode." ); } if ( ExecutableNode *executable = runTimeCast<ExecutableNode>( nIt->get() ) ) { executables.push_back( executable ); } else if ( const Box *box = runTimeCast<const Box>( nIt->get() ) ) { for ( RecursiveOutputPlugIterator plugIt( box ); plugIt != plugIt.end(); ++plugIt ) { Node *sourceNode = plugIt->get()->source<Plug>()->node(); if ( ExecutableNode *executable = runTimeCast<ExecutableNode>( sourceNode ) ) { executables.push_back( executable ); } } } else { throw IECore::Exception( getName().string() + ": Dispatched nodes must be ExecutableNodes or Boxes containing ExecutableNodes." ); } } // create the job directory now, so it's available in preDispatchSignal(). const Context *context = Context::current(); m_jobDirectory = createJobDirectory( context ); // this object calls this->preDispatchSignal() in its constructor and this->postDispatchSignal() // in its destructor, thereby guaranteeing that we always call this->postDispatchSignal(). DispatcherSignalGuard signalGuard( this, executables ); if ( signalGuard.cancelledByPreDispatch() ) { return; } std::vector<FrameList::Frame> frames; FrameListPtr frameList = frameRange( script, context ); frameList->asList( frames ); size_t i = 0; ExecutableNode::Tasks tasks; tasks.reserve( executables.size() * frames.size() ); for ( std::vector<FrameList::Frame>::const_iterator fIt = frames.begin(); fIt != frames.end(); ++fIt ) { for ( std::vector<ExecutableNodePtr>::const_iterator nIt = executables.begin(); nIt != executables.end(); ++nIt, ++i ) { ContextPtr frameContext = new Context( *context, Context::Borrowed ); frameContext->setFrame( *fIt ); tasks.push_back( ExecutableNode::Task( *nIt, frameContext ) ); } } TaskBatchPtr rootBatch = batchTasks( tasks ); if ( !rootBatch->requirements().empty() ) { doDispatch( rootBatch.get() ); } // inform the guard that the process has been completed, so it can pass this info to // postDispatchSignal(): signalGuard.success(); }
void Dispatcher::dispatch( const std::vector<NodePtr> &nodes ) const { if ( nodes.empty() ) { throw IECore::Exception( getName().string() + ": Must specify at least one node to dispatch." ); } std::vector<ExecutableNodePtr> executables; const ScriptNode *script = (*nodes.begin())->scriptNode(); for ( std::vector<NodePtr>::const_iterator nIt = nodes.begin(); nIt != nodes.end(); ++nIt ) { const ScriptNode *currentScript = (*nIt)->scriptNode(); if ( !currentScript || currentScript != script ) { throw IECore::Exception( getName().string() + ": Dispatched nodes must all belong to the same ScriptNode." ); } if ( ExecutableNode *executable = runTimeCast<ExecutableNode>( nIt->get() ) ) { executables.push_back( executable ); } else if ( const Box *box = runTimeCast<const Box>( nIt->get() ) ) { for ( RecursiveOutputPlugIterator plugIt( box ); plugIt != plugIt.end(); ++plugIt ) { Node *sourceNode = plugIt->get()->source<Plug>()->node(); if ( ExecutableNode *executable = runTimeCast<ExecutableNode>( sourceNode ) ) { executables.push_back( executable ); } } } else { throw IECore::Exception( getName().string() + ": Dispatched nodes must be ExecutableNodes or Boxes containing ExecutableNodes." ); } } if ( preDispatchSignal()( this, executables ) ) { /// \todo: communicate the cancellation to the user return; } const Context *context = Context::current(); std::vector<FrameList::Frame> frames; FrameListPtr frameList = frameRange( script, context ); frameList->asList( frames ); size_t i = 0; ExecutableNode::Tasks tasks; tasks.reserve( executables.size() * frames.size() ); for ( std::vector<FrameList::Frame>::const_iterator fIt = frames.begin(); fIt != frames.end(); ++fIt ) { for ( std::vector<ExecutableNodePtr>::const_iterator nIt = executables.begin(); nIt != executables.end(); ++nIt, ++i ) { ContextPtr frameContext = new Context( *context, Context::Borrowed ); frameContext->setFrame( *fIt ); tasks.push_back( ExecutableNode::Task( *nIt, frameContext ) ); } } TaskBatchPtr rootBatch = batchTasks( tasks ); if ( !rootBatch->requirements().empty() ) { doDispatch( rootBatch.get() ); } postDispatchSignal()( this, executables ); }