std::wstring get_call_stack() { class log_call_stack_walker : public stack_walker { std::string str_; public: log_call_stack_walker() : stack_walker() {} std::string flush() { return std::move(str_); } protected: virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) { } virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) { } virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) { } virtual void OnOutput(LPCSTR szText) { std::string str = szText; if(str.find("internal::get_call_stack") == std::string::npos && str.find("stack_walker::ShowCallstack") == std::string::npos) str_ += std::move(str); } }; static tbb::enumerable_thread_specific<log_call_stack_walker> walkers; try { auto& walker = walkers.local(); walker.ShowCallstack(); return u16(walker.flush()); } catch(...) { return L"!!!"; } }
void DependencyNode::propagateDirtiness( Plug *plugToDirty ) { // we're not able to signal anything if there's no node, so just early out Node *node = plugToDirty->ancestor<Node>(); if( !node ) { return; } // we don't emit dirtiness immediately for each plug as we traverse the // dependency graph for two reasons : // // - we don't want to emit dirtiness for the same plug more than once // - we don't want to emit dirtiness while the graph may still be being // rewired by slots connected to plugSetSignal() or plugInputChangedSignal() // // instead we collect all the dirty plugs in a container as we traverse // the graph and only when the traversal is complete do we emit the plugDirtiedSignal(). // // the container used is stored per-thread as although it's illegal to be // monkeying with a script from multiple threads, it's perfectly legal to // be monkeying with a different script in each thread. DirtyPlugsContainer &dirtyPlugs = g_dirtyPlugsContainers.local(); // if the container is currently empty then we are at the start of a traversal, // and will emit plugDirtiedSignal() and empty the container before returning // from this function. if the container isn't empty then we are mid-traversal // and will just add to it. const bool emit = dirtyPlugs.empty(); Plug *p = plugToDirty; while( p ) { dirtyPlugs.insert( p ); p = p->parent<Plug>(); } // we only propagate dirtiness along leaf level plugs, because // they are the only plugs which can be the target of the affects(), // and compute() methods. if( !plugToDirty->isInstanceOf( (IECore::TypeId)CompoundPlugTypeId ) ) { DependencyNode *dependencyNode = IECore::runTimeCast<DependencyNode>( node ); if( dependencyNode ) { AffectedPlugsContainer affected; dependencyNode->affects( plugToDirty, affected ); for( AffectedPlugsContainer::const_iterator it=affected.begin(); it!=affected.end(); it++ ) { if( ( *it )->isInstanceOf( (IECore::TypeId)Gaffer::CompoundPlugTypeId ) ) { // DependencyNode::affects() implementations are only allowed to place leaf plugs in the outputs, // so we helpfully report any mistakes. dirtyPlugs.clear(); throw IECore::Exception( "Non-leaf plug " + (*it)->fullName() + " cannot be returned by affects()" ); } // cast is ok - AffectedPlugsContainer only holds const pointers so that // affects() can be const to discourage implementations from having side effects. propagateDirtiness( const_cast<Plug *>( *it ) ); } } for( Plug::OutputContainer::const_iterator it=plugToDirty->outputs().begin(), eIt=plugToDirty->outputs().end(); it!=eIt; ++it ) { propagateDirtiness( *it ); } } if( emit ) { for( size_t i = 0, e = dirtyPlugs.size(); i < e; ++i ) { Plug *plug = dirtyPlugs.get<1>()[i]; Node *node = plug->node(); if( node ) { node->plugDirtiedSignal()( plug ); } } dirtyPlugs.clear(); } }
static DirtyPlugs &local() { static tbb::enumerable_thread_specific<Plug::DirtyPlugs> g_dirtyPlugs; return g_dirtyPlugs.local(); }