/********************************************************************** ** This program is part of 'MOOSE', the ** Messaging Object Oriented Simulation Environment. ** Copyright (C) 2003-2009 Upinder S. Bhalla. and NCBS ** It is made available under the terms of the ** GNU Lesser General Public License version 2.1 ** See the file COPYING.LIB for the full notice. **********************************************************************/ #include <string> #include <algorithm> using namespace std; #include "header.h" #include "global.h" #include "SingleMsg.h" #include "DiagonalMsg.h" #include "OneToOneMsg.h" #include "OneToAllMsg.h" #include "SparseMatrix.h" #include "SparseMsg.h" #include "Shell.h" #include "Dinfo.h" #include "Wildcard.h" // Want to separate out this search path into the Makefile options #include "../scheduling/Clock.h" /*#ifdef USE_SBML #include "../sbml/MooseSbmlWriter.h" #include "../sbml/MooseSbmlReader.h" #endif */ const unsigned int Shell::OkStatus = ~0; const unsigned int Shell::ErrorStatus = ~1; bool Shell::isBlockedOnParser_ = 0; bool Shell::keepLooping_ = 1; unsigned int Shell::numCores_; unsigned int Shell::numNodes_; unsigned int Shell::myNode_; ProcInfo Shell::p_; unsigned int Shell::numAcks_ = 0; vector< unsigned int > Shell::acked_( 1, 0 ); bool Shell::doReinit_( 0 ); bool Shell::isParserIdle_( 0 ); double Shell::runtime_( 0.0 ); const Cinfo* Shell::initCinfo() { #ifdef ENABLE_LOGGER clock_t t = clock(); #endif //////////////////////////////////////////////////////////////// // Value Finfos //////////////////////////////////////////////////////////////// static ReadOnlyValueFinfo< Shell, bool > isRunning( "isRunning", "Flag: Checks if simulation is in progress", &Shell::isRunning ); static ValueFinfo< Shell, ObjId > cwe( "cwe", "Current working Element", &Shell::setCwe, &Shell::getCwe ); //////////////////////////////////////////////////////////////// // Dest Finfos: Functions handled by Shell //////////////////////////////////////////////////////////////// static DestFinfo handleUseClock( "useClock", "Deals with assignment of path to a given clock." " Arguments: path, field, tick number. ", new EpFunc4< Shell, string, string, unsigned int, unsigned int >( &Shell::handleUseClock ) ); static DestFinfo handleCreate( "create", "create( class, parent, newElm, name, numData, isGlobal )", new EpFunc6< Shell, string, ObjId, Id, string, NodeBalance, unsigned int >( &Shell::handleCreate ) ); static DestFinfo handleDelete( "delete", "When applied to a regular object, this function operates " "on the Id (element) specified by the ObjId argument. " "The function deletes the entire object " "array on this Id, including all dataEntries on it," "all its messages, and all its children. The DataIndex here " "is ignored, and all dataEntries are destroyed. \n" "When applied to a message: Destroys only that one specific " "message identified by the full ObjId. \n" "Args: ObjId\n", new EpFunc1< Shell, ObjId >( & Shell::destroy ) ); static DestFinfo handleAddMsg( "addMsg", "Makes a msg. Arguments are:" " msgtype, src object, src field, dest object, dest field", new EpFunc6< Shell, string, ObjId, string, ObjId, string, unsigned int > ( & Shell::handleAddMsg ) ); static DestFinfo handleQuit( "quit", "Stops simulation running and quits the simulator", new OpFunc0< Shell >( & Shell::handleQuit ) ); static DestFinfo handleMove( "move", "handleMove( Id orig, Id newParent ): " "moves an Element to a new parent", new EpFunc2< Shell, Id, ObjId >( & Shell::handleMove ) ); static DestFinfo handleCopy( "copy", "handleCopy( vector< Id > args, string newName, unsigned int nCopies, bool toGlobal, bool copyExtMsgs ): " " The vector< Id > has Id orig, Id newParent, Id newElm. " "This function copies an Element and all its children to a new parent." " May also expand out the original into nCopies copies." " Normally all messages within the copy tree are also copied. " " If the flag copyExtMsgs is true, then all msgs going out are also copied.", new EpFunc5< Shell, vector< ObjId >, string, unsigned int, bool, bool >( & Shell::handleCopy ) ); static DestFinfo setclock( "setclock", "Assigns clock ticks. Args: tick#, dt", new OpFunc2< Shell, unsigned int, double >( & Shell::doSetClock ) ); static Finfo* shellFinfos[] = { &setclock, //////////////////////////////////////////////////////////////// // Shared msg //////////////////////////////////////////////////////////////// // &master, // &worker, &handleCreate, &handleDelete, &handleCopy, &handleMove, &handleAddMsg, &handleQuit, &handleUseClock, }; static Dinfo< Shell > d; static Cinfo shellCinfo ( "Shell", Neutral::initCinfo(), shellFinfos, sizeof( shellFinfos ) / sizeof( Finfo* ), &d //new Dinfo< Shell >() ); #ifdef ENABLE_LOGGER float time = (float(clock() - t)/CLOCKS_PER_SEC); logger.initializationTime.push_back( time ); #endif return &shellCinfo; } static const Cinfo* shellCinfo = Shell::initCinfo(); Shell::Shell() : gettingVector_( 0 ), numGetVecReturns_( 0 ), cwe_( ObjId() ) { getBuf_.resize( 1, 0 ); } Shell::~Shell() { ; } #ifdef CYMOOSE /*----------------------------------------------------------------------------- * This function must create a fully functional Shell. Used in cython * interface. *-----------------------------------------------------------------------------*/ Shell* Shell::initShell() { Eref sheller = Id().eref(); Shell* shell = reinterpret_cast< Shell* >( sheller.data() ); return shell; } Id Shell::create(string type, string name, unsigned int numData , NodePolicy nodePolicy, unsigned int preferredNode ) { if(name.size() == 0) return doCreate(type, Id(), name, numData, nodePolicy, preferredNode); string::size_type pos = name.find_last_of('/'); string parentPath = name.substr(0, pos); ObjId parentObj = ObjId(parentPath); //cerr << "info: Creating Obj with parent : " << parentObj << endl; Id id = doCreate(type, parentObj, name.substr(pos+1) , numData, nodePolicy, preferredNode ); //cerr << " ++ with id " << id << endl; return id; } #endif /* ----- CYMOOSE ----- */ void Shell::setShellElement( Element* shelle ) { shelle_ = shelle; } /** * This is the version used by the parser. Acts as a blocking, * serial-like interface to a potentially multithread, multinode call. * Returns the new Id index upon success, otherwise returns Id(). * The data of the new Element is not necessarily allocated at this point, * that can be deferred till the global Instantiate or Reset calls. * Idea is that the model should be fully defined before load balancing. * */ Id Shell::doCreate( string type, ObjId parent, string name, unsigned int numData, NodePolicy nodePolicy, unsigned int preferredNode ) { const Cinfo* c = Cinfo::find( type ); if ( !isNameValid( name ) ) { stringstream ss; ss << "Shell::doCreate: bad character in name'" << name << "'. No Element created"; warning( ss.str() ); return Id(); } if ( c ) { if ( c->banCreation() ) { stringstream ss; ss << "Shell::doCreate: Cannot create an object of class '" << type << "' because it is an abstract base class or a FieldElement.\n"; warning( ss.str() ); return Id(); } Element* pa = parent.element(); if ( !pa ) { stringstream ss; ss << "Shell::doCreate: Parent Element'" << parent << "' not found. No Element created"; warning( ss.str() ); return Id(); } if ( Neutral::child( parent.eref(), name ) != Id() ) { stringstream ss; ss << "Shell::doCreate: Object with same name already present: '" << parent.path() << "/" << name << "'. No Element created"; warning( ss.str() ); return Id(); } // Get the new Id ahead of time and pass to all nodes. Id ret = Id::nextId(); NodeBalance nb( numData, nodePolicy, preferredNode ); // Get the parent MsgIndex ahead of time and pass to all nodes. unsigned int parentMsgIndex = OneToAllMsg::numMsg(); SetGet6< string, ObjId, Id, string, NodeBalance, unsigned int >::set( ObjId(), // Apply command to Shell "create", // Function to call. type, // class of new object parent, // Parent ret, // id of new object name, // name of new object nb, // Node balance configuration parentMsgIndex // Message index of child-parent msg. ); // innerCreate( type, parent, ret, name, numData, isGlobal ); return ret; } else { stringstream ss; ss << "Shell::doCreate: Class '" << type << "' not known. No Element created"; warning( ss.str() ); } return Id(); } bool Shell::doDelete( ObjId oid ) { SetGet1< ObjId >::set( ObjId(), "delete", oid ); /* Neutral n; n.destroy( i.eref(), 0 ); */ return true; } ObjId Shell::doAddMsg( const string& msgType, ObjId src, const string& srcField, ObjId dest, const string& destField ) { if ( !src.id.element() ) { cout << myNode_ << ": Error: Shell::doAddMsg: src not found" << endl; return ObjId(); } if ( !dest.id.element() ) { cout << myNode_ << ": Error: Shell::doAddMsg: dest not found" << endl; return ObjId(0, BADINDEX ); } const Finfo* f1 = src.id.element()->cinfo()->findFinfo( srcField ); if ( !f1 ) { cout << myNode_ << ": Shell::doAddMsg: Error: Failed to find field " << srcField << " on src: " << src.id.element()->getName() << endl; return ObjId(0, BADINDEX ); } const Finfo* f2 = dest.id.element()->cinfo()->findFinfo( destField ); if ( !f2 ) { cout << myNode_ << ": Shell::doAddMsg: Error: Failed to find field " << destField << " on dest: " << dest.id.element()->getName() << endl; cout << "Available fields are : " << endl << moose::mapToString<string, Finfo*>(dest.id.element()->cinfo()->finfoMap()); return ObjId( 0, BADINDEX ); } if ( ! f1->checkTarget( f2 ) ) { cout << myNode_ << ": Shell::doAddMsg: Error: Src/Dest Msg type mismatch: " << srcField << "/" << destField << endl; return ObjId( 0, BADINDEX ); } const Msg* m = innerAddMsg( msgType, src, srcField, dest, destField, 0 ); SetGet6< string, ObjId, string, ObjId, string, unsigned int >::set( ObjId(), // Apply command to Shell "addMsg", // Function to call. msgType, src, srcField, dest, destField, m->mid().dataIndex ); return m->mid(); // const Msg* m = innerAddMsg( msgType, src, srcField, dest, destField ); // return m->mid(); // return Msg::lastMsg()->mid(); } void Shell::doQuit() { SetGet0::set( ObjId(), "quit" ); } void Shell::doStart( double runtime, bool notify ) { Id clockId( 1 ); SetGet2< double, bool >::set( clockId, "start", runtime, notify ); } bool isDoingReinit() { static Id clockId( 1 ); assert( clockId.element() != 0 ); return ( reinterpret_cast< const Clock* >( clockId.eref().data() ) )->isDoingReinit(); } void Shell::doReinit( ) { Id clockId( 1 ); SetGet0::set( clockId, "reinit" ); } void Shell::doStop( ) { Id clockId( 1 ); SetGet0::set( clockId, "stop" ); } //////////////////////////////////////////////////////////////////////// void Shell::doSetClock( unsigned int tickNum, double dt ) { LookupField< unsigned int, double >::set( ObjId( 1 ), "tickDt", tickNum, dt ); // FIXME: // HACK: If clock 18 is being updated, make sure that clock 19 (streamer is also // updated with correct dt (10 or 100*dt). This is bit hacky. if( tickNum == 18 ) LookupField< unsigned int, double >::set( ObjId( 1 ), "tickDt" , tickNum + 1, max( 100 * dt, 10.0 ) ); } void Shell::doUseClock( string path, string field, unsigned int tick ) { unsigned int msgIndex = OneToAllMsg::numMsg(); SetGet4< string, string, unsigned int, unsigned int >::set( ObjId(), "useClock", path, field, tick, msgIndex ); // innerUseClock( path, field, tick); } /** * Write given model to SBML file. Returns success value. */ /* int Shell::doWriteSBML( const string& fname, const string& modelpath ) { #ifdef USE_SBML moose::SbmlWriter sw; int ret = sw.write( fname, modelpath ); return ret; #else cerr << "Shell::WriteSBML: This copy of MOOSE has not been compiled with SBML writing support.\n"; return -2; #endif } */ /** * read given SBML model to moose. Returns success value. */ /* Id Shell::doReadSBML( const string& fname, const string& modelpath, const string& solverclass ) { #ifdef USE_SBML moose::SbmlReader sr; return sr.read( fname, modelpath,solverclass); #else cerr << "Shell::ReadSBML: This copy of MOOSE has not been compiled with SBML reading support.\n"; return Id(); #endif } */ //////////////////////////////////////////////////////////////////////// void Shell::doMove( Id orig, ObjId newParent ) { if ( orig == Id() ) { cout << "Error: Shell::doMove: Cannot move root Element\n"; return; } if ( newParent.element() == 0 ) { cout << "Error: Shell::doMove: Cannot move object to null parent \n"; return; } if ( Neutral::isDescendant( newParent, orig ) ) { cout << "Error: Shell::doMove: Cannot move object to descendant in tree\n"; return; } const string& name = orig.element()->getName(); if ( Neutral::child( newParent.eref(), name ) != Id() ) { stringstream ss; ss << "Shell::doMove: Object with same name already present: '" << newParent.path() << "/" << name << "'. Move failed."; warning( ss.str() ); return; } SetGet2< Id, ObjId >::set( ObjId(), "move", orig, newParent ); // innerMove( orig, newParent ); } bool extractIndex( const string& s, unsigned int& index ) { vector< unsigned int > open; vector< unsigned int > close; index = 0; if ( s.length() == 0 ) // a plain slash is OK return true; if ( s[0] == '[' ) // Cannot open with a brace return false; for ( unsigned int i = 0; i < s.length(); ++i ) { if ( s[i] == '[' ) open.push_back( i+1 ); else if ( s[i] == ']' ) close.push_back( i ); } if ( open.size() != close.size() ) return false; if ( open.size() == 0 ) return true; // the index was set already to zero. int j = atoi( s.c_str() + open[0] ); if ( j >= 0 ) { index = j; return true; } return false; } /** * Static func to subdivide a string at the specified separator. */ bool Shell::chopString( const string& path, vector< string >& ret, char separator ) { // /foo/bar/zod // foo/bar/zod // ./foo/bar/zod // ../foo/bar/zod // . // / // .. ret.resize( 0 ); if ( path.length() == 0 ) return 1; // Treat it as an absolute path bool isAbsolute = 0; string temp = path; if ( path[0] == separator ) { isAbsolute = 1; if ( path.length() == 1 ) return 1; temp = temp.substr( 1 ); } string::size_type pos = temp.find_first_of( separator ); ret.push_back( temp.substr( 0, pos ) ); while ( pos != string::npos ) { temp = temp.substr( pos + 1 ); if ( temp.length() == 0 ) break; pos = temp.find_first_of( separator ); ret.push_back( temp.substr( 0, pos ) ); } return isAbsolute; } /** * Static func to check if an object name is legal. True if legal. */ bool Shell::isNameValid( const string& name ) { return ( name.length() > 0 && name.find_first_of( "[] #?\"/\\" ) == string::npos ); } /** * static func. * * Example: /foo/bar[10]/zod[3] would return: * ret: {"foo", "bar", "zod" } * index: { 0, 10, 3 } */ bool Shell::chopPath( const string& path, vector< string >& ret, vector< unsigned int >& index ) { bool isAbsolute = chopString( path, ret, '/' ); if ( isAbsolute ) { index.clear(); } else { index.clear(); } for ( unsigned int i = 0; i < ret.size(); ++i ) { index.push_back( 0 ); if ( ret[i] == "." ) continue; if ( ret[i] == ".." ) { continue; } if ( !extractIndex( ret[i], index[i] ) ) { cout << "Error: Shell::chopPath: Failed to parse indices in path '" << path << "'\n"; ret.resize( 0 ); index.resize( 0 ); return isAbsolute; } size_t pos = ret[i].find_first_of( '[' ); if ( ret[i].find_first_of( '[' ) != string::npos ) ret[i] = ret[i].substr( 0, pos ); } return isAbsolute; } /* /// non-static func. Fallback which treats index brackets as part of /// name string, and does not try to extract integer indices. ObjId Shell::doFindWithoutIndexing( const string& path ) const { Id curr = Id(); vector< string > names; vector< vector< unsigned int > > indices; bool isAbsolute = chopString( path, names, '/' ); if ( !isAbsolute ) curr = cwe_; for ( vector< string >::iterator i = names.begin(); i != names.end(); ++i ) { if ( *i == "." ) { } else if ( *i == ".." ) { curr = Neutral::parent( curr.eref() ).id; } else { curr = Neutral::child( curr.eref(), *i ); } } assert( curr.element() ); assert( curr.element()->dataHandler() ); return ObjId( curr, 0 ); } */ /// non-static func. Returns the Id found by traversing the specified path. ObjId Shell::doFind( const string& path ) const { if ( path == "/" || path == "/root" ) return ObjId(); ObjId curr; vector< string > names; vector< unsigned int > indices; bool isAbsolute = chopPath( path, names, indices ); assert( names.size() == indices.size() ); if ( !isAbsolute ) curr = cwe_; for ( unsigned int i = 0; i < names.size(); ++i ) { if ( names[i] == "." ) { } else if ( names[i] == ".." ) { curr = Neutral::parent( curr.eref() ); } else { ObjId pa = curr; curr = Neutral::child( curr.eref(), names[i] ); if ( curr == ObjId() ) // Neutral::child returned Id(), ie, bad. return ObjId( 0, BADINDEX ); if ( curr.element()->hasFields() ) { curr.dataIndex = pa.dataIndex; curr.fieldIndex = indices[i]; } else { curr.dataIndex = indices[i]; if ( curr.element()->numData() <= curr.dataIndex ) return ObjId( 0, BADINDEX ); } } } assert( curr.element() ); if ( curr.element()->numData() <= curr.dataIndex ) return ObjId( 0, BADINDEX ); if ( curr.fieldIndex > 0 && !curr.element()->hasFields() ) return ObjId( 0, BADINDEX ); return curr; } //////////////////////////////////////////////////////////////// // DestFuncs //////////////////////////////////////////////////////////////// string Shell::doVersion() { return MOOSE_VERSION; } void Shell::setCwe( ObjId val ) { cwe_ = val; } ObjId Shell::getCwe() const { return cwe_; } bool Shell::isRunning() const { static Id clockId( 1 ); assert( clockId.element() != 0 ); return ( reinterpret_cast< const Clock* >( clockId.eref().data() ) )->isRunning(); } /** * This function handles the message request to create an Element. * This request specifies the Id of the new Element and is handled on * all nodes. * * In due course we also have to set up the node decomposition of the * Element, but for now the num indicates the total # of array entries. * This gets a bit complicated if the Element is a multidim array. */ void Shell::handleCreate( const Eref& e, string type, ObjId parent, Id newElm, string name, NodeBalance nb, unsigned int parentMsgIndex ) { innerCreate( type, parent, newElm, name, nb, parentMsgIndex ); } /** * Static utility function. Attaches child element to parent element. * Must only be called from functions executing in parallel on all nodes, * as it does a local message addition * MsgIndex is needed to be sure that the same msg identifies parent-child * connection on all nodes. */ bool Shell::adopt( ObjId parent, Id child, unsigned int msgIndex ) { static const Finfo* pf = Neutral::initCinfo()->findFinfo( "parentMsg" ); // static const DestFinfo* pf2 = dynamic_cast< const DestFinfo* >( pf ); // static const FuncId pafid = pf2->getFid(); static const Finfo* f1 = Neutral::initCinfo()->findFinfo( "childOut" ); assert( !( child.element() == 0 ) ); assert( !( child == Id() ) ); assert( !( parent.element() == 0 ) ); Msg* m = new OneToAllMsg( parent.eref(), child.element(), msgIndex ); assert( m ); // cout << myNode_ << ", Shell::adopt: mid = " << m->mid() << ", pa =" << parent << "." << parent()->getName() << ", kid=" << child << "." << child()->getName() << "\n"; if ( !f1->addMsg( pf, m->mid(), parent.element() ) ) { cout << "move: Error: unable to add parent->child msg from " << parent.element()->getName() << " to " << child.element()->getName() << "\n"; return 0; } return 1; } /// Adaptor for above function. Also static function. bool Shell::adopt( Id parent, Id child, unsigned int msgIndex ) { return adopt( ObjId( parent ), child, msgIndex ); } /** * This function actually creates the object. Runs on all nodes. * Assumes we've already done all the argument checking. */ void Shell::innerCreate( string type, ObjId parent, Id newElm, string name, const NodeBalance& nb, unsigned int msgIndex ) // unsigned int numData, bool isGlobal { const Cinfo* c = Cinfo::find( type ); if ( c ) { Element* ret = 0; switch ( nb.policy ) { case MooseGlobal: ret = new GlobalDataElement( newElm, c, name, nb.numData ); break; case MooseBlockBalance: ret = new LocalDataElement( newElm, c, name, nb.numData ); break; case MooseSingleNode: cout << "Error: Shell::innerCreate: Yet to implement SingleNodeDataElement. Making BlockBalance.\n"; ret = new LocalDataElement( newElm, c, name, nb.numData ); // ret = new SingleNodeDataElement( newElm, c, name, numData, nb.preferredNode ); break; }; assert( ret ); adopt( parent, newElm, msgIndex ); ret->setTick( Clock::lookupDefaultTick( c->name() ) ); } else { assert( 0 ); } } void Shell::destroy( const Eref& e, ObjId oid) { Neutral *n = reinterpret_cast< Neutral* >( e.data() ); assert( n ); // cout << myNode_ << ": Shell::destroy done for element id: " << eid << ", name = " << eid.element()->getName() << endl; n->destroy( oid.eref(), 0 ); if ( cwe_.id == oid.id ) cwe_ = ObjId(); } /** * Wrapper function, that does the ack. Other functions also use the * inner function to build message trees, so we don't want it to emit * multiple acks. */ void Shell::handleAddMsg( const Eref& e, string msgType, ObjId src, string srcField, ObjId dest, string destField, unsigned int msgIndex ) { // Node 0 will have already called innerAddMsg to get the msgIndex if ( myNode() != 0 ) innerAddMsg( msgType, src, srcField, dest, destField, msgIndex ); /* if ( innerAddMsg( msgType, src, srcField, dest, destField ) ) ack()->send( Eref( shelle_, 0 ), Shell::myNode(), OkStatus ); else ack()->send( Eref( shelle_, 0), Shell::myNode(), ErrorStatus ); */ } /** * The actual function that adds messages. Does NOT send an ack. * The msgIndex specifies the index on which to place this message. If the * value is zero it does an automatic placement. * Returns zero on failure. */ const Msg* Shell::innerAddMsg( string msgType, ObjId src, string srcField, ObjId dest, string destField, unsigned int msgIndex ) { /* cout << myNode_ << ", Shell::handleAddMsg: " << msgType << ", " << mid << ", src =" << src << "." << srcField << ", dest =" << dest << "." << destField << "\n"; */ const Finfo* f1 = src.id.element()->cinfo()->findFinfo( srcField ); if ( f1 == 0 ) return 0; const Finfo* f2 = dest.id.element()->cinfo()->findFinfo( destField ); if ( f2 == 0 ) return 0; // Should have been done before msgs request went out. assert( f1->checkTarget( f2 ) ); Msg *m = 0; if ( msgType == "diagonal" || msgType == "Diagonal" ) { m = new DiagonalMsg( src.id.element(), dest.id.element(), msgIndex ); } else if ( msgType == "sparse" || msgType == "Sparse" ) { m = new SparseMsg( src.id.element(), dest.id.element(), msgIndex ); } else if ( msgType == "Single" || msgType == "single" ) { m = new SingleMsg( src.eref(), dest.eref(), msgIndex ); } else if ( msgType == "OneToAll" || msgType == "oneToAll" ) { m = new OneToAllMsg( src.eref(), dest.id.element(), msgIndex ); } else if ( msgType == "AllToOne" || msgType == "allToOne" ) { m = new OneToAllMsg( dest.eref(), src.id.element(), msgIndex ); // Little hack. } else if ( msgType == "OneToOne" || msgType == "oneToOne" ) { m = new OneToOneMsg( src.eref(), dest.eref(), msgIndex ); } else { cout << myNode_ << ": Error: Shell::handleAddMsg: msgType not known: " << msgType << endl; return m; } if ( m ) { if ( f1->addMsg( f2, m->mid(), src.id.element() ) ) { return m; } delete m; m = 0; } cout << myNode_ << ": Error: Shell::handleAddMsg: Unable to make/connect Msg: " << msgType << " from " << src.id.element()->getName() << " to " << dest.id.element()->getName() << endl; return m; } bool Shell::innerMove( Id orig, ObjId newParent ) { static const Finfo* pf = Neutral::initCinfo()->findFinfo( "parentMsg" ); static const DestFinfo* pf2 = dynamic_cast< const DestFinfo* >( pf ); static const FuncId pafid = pf2->getFid(); static const Finfo* f1 = Neutral::initCinfo()->findFinfo( "childOut" ); assert( !( orig == Id() ) ); assert( !( newParent.element() == 0 ) ); ObjId mid = orig.element()->findCaller( pafid ); Msg::deleteMsg( mid ); Msg* m = new OneToAllMsg( newParent.eref(), orig.element(), 0 ); assert( m ); if ( !f1->addMsg( pf, m->mid(), newParent.element() ) ) { cout << "move: Error: unable to add parent->child msg from " << newParent.element()->getName() << " to " << orig.element()->getName() << "\n"; return 0; } return 1; } void Shell::handleMove( const Eref& e, Id orig, ObjId newParent ) { innerMove( orig, newParent ); /* if ( innerMove( orig, newParent ) ) ack()->send( Eref( shelle_, 0 ), Shell::myNode(), OkStatus ); else ack()->send( Eref( shelle_, 0 ), Shell::myNode(), ErrorStatus ); */ } void insertSharedMsgs( const Finfo* f, const Element* e, vector< ObjId >& msgs ) { const SharedFinfo* sf = dynamic_cast< const SharedFinfo *>( f ); if ( sf ) { for ( vector< Finfo* >::const_iterator j = sf->dest().begin(); j != sf->dest().end(); ++j ) { DestFinfo* df = dynamic_cast< DestFinfo* >( *j ); assert( df ); FuncId fid = df->getFid(); // These are the messages to be zapped vector< ObjId > caller; if ( e->getInputMsgs( caller, fid ) > 0 ) { msgs.insert( msgs.end(), caller.begin(), caller.end() ); } } } } // Static function void Shell::dropClockMsgs( const vector< ObjId >& list, const string& field ) { vector< ObjId > msgs; // These are the messages to delete. for ( vector< ObjId >::const_iterator i = list.begin(); i != list.end(); ++i ) { // Sanity check: shouldn't try to operate on already deleted objects if ( i->element() ) { const Finfo* f = i->element()->cinfo()->findFinfo( field ); const DestFinfo* df = dynamic_cast< const DestFinfo *>( f ); if ( df ) { FuncId fid = df->getFid(); // These are the messages to be zapped vector< ObjId > caller; if ( i->element()->getInputMsgs( caller, fid ) > 0 ) { msgs.insert( msgs.end(), caller.begin(), caller.end() ); } } else { insertSharedMsgs( f, i->element(), msgs ); } } } // Do the unique/erase bit. My favourite example of C++ hideousity. sort( msgs.begin(), msgs.end() ); msgs.erase( unique( msgs.begin(), msgs.end() ), msgs.end() ); // Delete them. for( vector< ObjId >::iterator i = msgs.begin(); i != msgs.end(); ++i ) Msg::deleteMsg( *i ); } // Non-static function. The innerAddMsg needs the shell. void Shell::addClockMsgs( const vector< ObjId >& list, const string& field, unsigned int tick, unsigned int msgIndex ) { if ( !Id( 1 ).element() ) return; ObjId clockId( 1 ); dropClockMsgs( list, field ); // Forbid duplicate PROCESS actions. for ( vector< ObjId >::const_iterator i = list.begin(); i != list.end(); ++i ) { if ( i->element() ) { stringstream ss; ss << "proc" << tick; const Msg* m = innerAddMsg( "OneToAll", clockId, ss.str(), *i, field, msgIndex++ ); if ( m ) i->element()->innerSetTick( tick ); } } } bool Shell::innerUseClock( string path, string field, unsigned int tick, unsigned int msgIndex ) { vector< ObjId > list; wildcardFind( path, list ); // By default scans only Elements. if ( list.size() == 0 ) { // cout << "Warning: Shell::innerUseClock: no Elements found on path " << path << endl; return 0; } // string tickField = "proc"; // Hack to get around a common error. if ( field.substr( 0, 4 ) == "proc" || field.substr( 0, 4 ) == "Proc" ) field = "proc"; if ( field.substr( 0, 4 ) == "init" || field.substr( 0, 4 ) == "Init" ) field = "init"; addClockMsgs( list, field, tick, msgIndex ); for ( vector< ObjId >::iterator i = list.begin(); i != list.end(); ++i ) i->id.element()->innerSetTick( tick ); return 1; } void Shell::handleUseClock( const Eref& e, string path, string field, unsigned int tick, unsigned int msgIndex ) { // cout << q->getProcInfo()->threadIndexInGroup << ": in Shell::handleUseClock with path " << path << endl << flush; innerUseClock( path, field, tick, msgIndex ); /* if ( innerUseClock( path, field, tick ) ) ack()->send( Eref( shelle_, 0 ), Shell::myNode(), OkStatus ); else ack()->send( Eref( shelle_, 0 ), Shell::myNode(), ErrorStatus ); */ } void Shell::handleQuit() { Shell::keepLooping_ = 0; } // Static function bool Shell::keepLooping() { return keepLooping_; } void Shell::warning( const string& text ) { LOG( moose::warning, text ); } void Shell::error( const string& text ) { cout << "Error: Shell:: " << text << endl; } /* void Shell::wildcard( const string& path, vector< ObjId >& list ) { wildcardFind( path, list ); } */ /** * @brief Clean-up MOOSE before shutting down. This function is called whenever * keyboard interrupt terminates the simulation. */ void Shell::cleanSimulation() { Eref sheller = Id().eref(); Shell* s = reinterpret_cast< Shell* >( sheller.data() ); vector< Id > kids; Neutral::children( sheller, kids ); for ( vector< Id >::iterator i = kids.begin(); i != kids.end(); ++i ) { if ( i->value() > 4 ) { LOG( moose::debug , "Shell::cleanSimulation: deleted cruft at " << i->value() << ": " << i->path()); s->doDelete( *i ); } } LOG( moose::info, "Cleaned up!"); }