Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

sim.cc

Go to the documentation of this file.
00001 #include "sim.h"
00002 #include "Main.h"
00003 #include "Motion.h"
00004 #include "SoundPlay.h"
00005 #include "Simulator.h"
00006 #include "SharedGlobals.h"
00007 #include "SimConfig.h"
00008 #include "local/DataSource.h"
00009 #include "IPC/SharedObject.h"
00010 #include "IPC/RegionRegistry.h"
00011 #include "IPC/FailsafeThread.h"
00012 #include "Shared/Config.h"
00013 #include "Shared/string_util.h"
00014 #include "Shared/StackTrace.h"
00015 #include "Shared/zignor.h"
00016 #include "Shared/RobotInfo.h"
00017 #include "IPC/SemaphoreManager.h"
00018 #include "Events/EventRouter.h"
00019 #include "Motion/Kinematics.h"
00020 #include <iostream>
00021 #include <sstream>
00022 #include <iomanip>
00023 #include <sys/ipc.h>
00024 #include <signal.h>
00025 #include <unistd.h>
00026 #include <sys/wait.h>
00027 #include <sys/stat.h>
00028 #include <errno.h>
00029 #include <pwd.h>
00030 #include <algorithm>
00031 #include <fcntl.h>
00032 
00033 #ifndef DISABLE_READLINE
00034 #  include <readline/readline.h>
00035 #endif
00036 
00037 #ifdef __APPLE__
00038 #  include <CoreFoundation/CoreFoundation.h>
00039 #endif
00040 
00041 #ifndef TEKKOTSU_SHM_STYLE
00042 #  error TEKKOTSU_SHM_STYLE unset!
00043 #endif
00044 
00045 using namespace std;
00046 
00047 INSTANTIATE_NAMEDENUMERATION_STATICS(SharedGlobals::runlevel_t);
00048 
00049 const char * const sim::usage=
00050 "[-h|--help] [-c|--config config-file] [-s|--seed s1,s2,s3] [cmd1 cmd2 ...]\n"
00051 "\n"
00052 "Commands passed as arguments are commonly of the form var=val, but can\n"
00053 "also be any valid simulator command, such as 'freeze'.  If you wish to\n"
00054 "pass a multi-word command, encase the command in quotes.\n"
00055 "\n"
00056 "The first --seed argument is passed to srand and srandom, the second\n"
00057 "and third are passed to the zignor library for normally distributed"
00058 "random numbers.";
00059   
00060 //only true for the first process -- children should set this to false when they fork
00061 bool sim::original=true;
00062   
00063 SimConfig sim::config;
00064 vector<string> sim::cmdlineArgs;
00065 pid_t sim::child=static_cast<pid_t>(-1);
00066 bool sim::showRunlevels=false;
00067 termios sim::ttyMode;
00068 vector<Thread*> sim::primaries;
00069 sim::ConfigErrorCheck sim::cfgCheck;
00070 
00071 #ifndef __APPLE__
00072 //! a wrapper for one of the 'major' processes, which would otherwise be forked as its own process if SimConfig::multiprocess was true
00073 template<typename T>
00074 struct PrimaryThread : public Thread {
00075   virtual void* run() { sim::manage_process<T>(); return NULL; }
00076 };
00077 #else
00078 static Thread::Lock primaryThreadLock;
00079 //! a wrapper for one of the 'major' processes, which would otherwise be forked as its own process if SimConfig::multiprocess was true
00080 /*! The Mac OS X version of this thread does a reference count and stops the Main RunLoop on the last one */
00081 template<typename T>
00082 struct PrimaryThread : public Thread {
00083   virtual void* run() {
00084     sim::manage_process<T>();
00085     {
00086       MarkScope lock(primaryThreadLock);
00087       vector<Thread*>::iterator it = find(sim::primaries.begin(),sim::primaries.end(),this);
00088       if(it==sim::primaries.end()) {
00089         std::cerr << "WARNING could not find thread for " << ProcessID::getIDStr() << " in sim::primaries" << std::endl;
00090       } else {
00091         sim::primaries.erase(it);
00092         if(sim::primaries.empty())
00093           CFRunLoopStop(CFRunLoopGetMain());
00094       }
00095     }
00096     return NULL;
00097   }
00098   virtual void dereference() {
00099     delete this;
00100   }
00101 };
00102 #endif
00103   
00104 /* Although I generally dislike the "can't have main function without a class declaration" style of java,
00105  * sometimes it does come in handy.  See the class notes for sim for more information. */
00106 int main(int argc, const char* argv[]) {
00107   
00108   //This should match the ID of the process sent to manage_process by sim::run()
00109   // *must* be done before we create any shared regions to provide proper reference counting
00110   ProcessID::setID(ProcessID::SimulatorProcess);
00111       
00112   //initialize some threading globals
00113   Thread::initMainThread();
00114   
00115 #if TEKKOTSU_SHM_STYLE==POSIX_SHM
00116 #ifndef USE_UNBACKED_SHM
00117   //append username to shared memory root avoid permission denied
00118   struct passwd * pw=getpwuid(getuid());
00119   if(pw!=NULL) {
00120     if(RCRegion::shmRoot[RCRegion::shmRoot.size()-1]=='/') {
00121       RCRegion::shmRoot[RCRegion::shmRoot.size()-1]='-';
00122       RCRegion::shmRoot+=pw->pw_name;
00123       RCRegion::shmRoot+='/';
00124     } else {
00125       RCRegion::shmRoot+=pw->pw_name;
00126     }
00127   }
00128 #endif
00129 #elif TEKKOTSU_SHM_STYLE==NO_SHM
00130   sim::cfgCheck.holdMultiprocess();
00131 #endif
00132 
00133   {
00134     sim s;
00135     if(!s.processCommandLine(argc,argv))
00136       return 2;
00137     //now the real meat begins
00138     if(!s.run()) //when this eventually returns, we're done (and all our children forks as well)
00139       return 1;
00140     return 0;
00141   }
00142 }
00143   
00144 sim::sim()
00145   : mutexSemMgr(), glob(), subj(),
00146   srandSeed(1), zigSeed1(12345), zigSeed2(54321)
00147 {
00148 #ifndef DISABLE_READLINE
00149   /* get current settings so we can manually restore in case of error because readline is a POS */
00150   int fd = getTermFD();
00151   if(fd<0 || tcgetattr(fd, &ttyMode) == -1)
00152     cerr << "tcgetattr failed, could not store terminal state in case of error shutdown" << endl;
00153   close(fd);
00154 #endif
00155   
00156   //what's the granularity of usleep on this platform?
00157   MutexLockBase::usleep_granularity=measure_usleep_cost();
00158   //use our own custom get_time routine
00159   project_get_time::get_time_callback=&sim_get_time;
00160   project_get_time::get_timeScale_callback=&sim_getTimeScale;
00161     
00162   ProjectInterface::sendCommand=Simulator::sendCommand;
00163     
00164   //setup signal handlers
00165   signal(SIGHUP, handle_signal);
00166   signal(SIGINT, handle_signal);
00167   signal(SIGQUIT, handle_signal);
00168   signal(SIGILL, handle_signal);
00169   signal(SIGABRT, handle_signal);
00170   signal(SIGFPE, handle_signal);
00171   signal(SIGBUS, handle_signal);
00172   signal(SIGSEGV, handle_signal);
00173   signal(SIGSYS, handle_signal);
00174   signal(SIGPIPE, SIG_IGN);
00175 #ifdef __linux__
00176   // sigchld is ignored because of the way we fork processes for sound output
00177   // see SoundManager::playFile for more details
00178   signal(SIGCHLD, SIG_IGN);
00179 #endif
00180   signal(SIGTERM, handle_signal);
00181   atexit(handle_exit);
00182     
00183 #if TEKKOTSU_SHM_STYLE!=NO_SHM
00184   //Set up mutex's semaphore manager
00185   MutexLockBase::setSemaphoreManager(&(*mutexSemMgr));
00186 #endif
00187   //Set up MessageQueue's semaphore manager
00188   MessageQueueBase::setSemaphoreManager(&(*mutexSemMgr));
00189   
00190   //Set up shared global parameters -- e.g. clock and runlevel info
00191   globals = &(*glob);
00192   sim::config.addEntry("WaitForSensors",globals->waitForSensors,"If true, wait for initial sensor readings before triggering the startup behavior or starting the motion polling thread.  On some platforms (e.g. Dynamixel based actuators), sensed output values can be used to initialize output positions.  On others, you may be unable to get any feedback, or can only expect feedback if the robot was left running and the executable is reconnecting (e.g. SSC-32).");
00193   sim::config.addEntry("Speed",globals->timeScale,"The speed at which to run the simulation, as a multiple of \"real-time\".\nFor example, '1' is normal, '0.5' is half-speed, '0' is paused.\nAny negative value requests full-speed mode, where the clock is moved as fast as processing (or manual control) allows.");
00194   globals->simulatorTime=sim::config.initSimTime;
00195   sim::config.addEntry("Motion",globals->motion,"Parameters for motion simulation");
00196   sim::config.addEntry("Sensors",globals->sensors,"Parameters for sensor updates");
00197   sim::config.addEntry("Vision",globals->vision,"Parameters for camera frames");
00198   DataSource::setSensorState(&globals->sensorState);
00199     
00200   //Set up the subject registration area
00201   ipc_setup = &(*subj);
00202     
00203   //everyone uses config and erouter, might as well set it up here
00204 	::config = new Config();
00205 	::config->setFileSystemRoot("ms");
00206   if(::config->loadFile("config/tekkotsu.xml")==0) {
00207     if(::config->loadFile("config/tekkotsu.cfg")==0)
00208       std::cerr << std::endl << " *** ERROR: Could not load configuration file config/tekkotsu.xml *** " << std::endl << std::endl;
00209     else
00210       std::cerr << "Successfully imported settings from old-format tekkotsu.cfg" << std::endl;
00211   }
00212   if(::config->loadFile("config/sim_ovrd.xml")==0)
00213     if(::config->loadFile("config/sim_ovrd.cfg")!=0)
00214       std::cerr << "Successfully imported settings from old-format simulator.cfg" << std::endl;
00215   ::erouter = new EventRouter;
00216   
00217   //we won't have sensor values yet, but the system clock is good enough
00218   if(::config->main.seed_rng) {
00219     struct timeval tp;
00220     gettimeofday(&tp,NULL);
00221     zigSeed1 = tp.tv_sec + tp.tv_usec;
00222     zigSeed2 = tp.tv_usec;
00223     
00224     union {
00225       double t;
00226       unsigned int tm[2];
00227     };
00228     t=TimeET().Value(); //current time with nanosecond resolution
00229     srandSeed=tm[0]+tm[1];
00230   }
00231 }
00232   
00233 bool sim::processCommandLine(int argc, const char* argv[]) {
00234   int i=0;
00235   
00236   //try to load the configuration file
00237   string halconfigfile;
00238   halconfigfile.append("hal-").append(RobotName).append(".plist");
00239   struct stat sb;
00240   if(stat(halconfigfile.c_str(),&sb)==0) {
00241     if(!sim::config.loadFile(halconfigfile.c_str())) {
00242       cerr << "Error loading default configuration file '" << halconfigfile << "', may be malformed." << endl;
00243       return false;
00244     }
00245   } else {
00246     // no user settings available, load the defaults
00247     string defaultfile;
00248     defaultfile.assign("defaults/hal-common.plist");
00249     if(stat(defaultfile.c_str(),&sb)==0) {
00250       if(!sim::config.loadFile(defaultfile.c_str())) {
00251         cerr << "Error loading defaults file '" << defaultfile << "', may be malformed." << endl;
00252         return false;
00253       }
00254     }
00255     defaultfile.assign("defaults/hal-").append(RobotName).append(".plist");
00256     if(stat(defaultfile.c_str(),&sb)==0) {
00257       if(!sim::config.loadFile(defaultfile.c_str())) {
00258         cerr << "Error loading defaults file '" << defaultfile << "', may be malformed." << endl;
00259         return false;
00260       }
00261     }
00262     sim::config.setLastFile(halconfigfile); // don't want to save over the defaults file
00263   }
00264   
00265   //set the prompt from the binary name
00266   /*config.cmdPrompt=argv[i];
00267   if(config.cmdPrompt.rfind('/')!=string::npos)
00268     config.cmdPrompt=config.cmdPrompt.substr(config.cmdPrompt.rfind('/')+1);
00269   config.cmdPrompt+="> ";*/
00270   
00271   //set the prompt from the robot name
00272   config.cmdPrompt="HAL:";
00273   config.cmdPrompt.append(RobotName).append("> ");
00274   
00275   //now run through the rest of the arguments
00276   for(i++; i<argc; i++) {
00277     
00278     if(!strcmp(argv[i],"-h") || !strcmp(argv[i],"--help")) {
00279       cerr << "Usage:\n" << argv[0] << " " << usage << endl;
00280       return false;
00281       
00282     } else if(!strcmp(argv[i],"-c") || !strcmp(argv[i],"--config")) {
00283       if(i==argc-1) {
00284         std::cerr << "ERROR: Missing --config filename argument" << std::endl;
00285         return false;
00286       }
00287       if(!sim::config.loadFile(argv[++i]))
00288         return false;
00289       
00290     } else if(!strcmp(argv[i],"-s") || !strcmp(argv[i],"--seed") || !strcmp(argv[i],"--seeds")) {
00291       if(i==argc-1) {
00292         std::cerr << "Missing --seed argument" << std::endl;
00293         return false;
00294       }
00295       std::string s = argv[++i];
00296       std::replace(s.begin(),s.end(),',',' ');
00297       std::stringstream ss(s);
00298       ss >> srandSeed >> zigSeed1 >> zigSeed2;
00299       
00300     } else if(strchr(argv[i],'=')!=NULL) {
00301       string value=string_util::trim(strchr(argv[i],'=')+1);
00302       string key=string_util::trim(string(argv[i],strchr(argv[i],'=')-argv[i]));
00303       plist::ObjectBase* ob=sim::config.resolveEntry(key);
00304       if(ob==NULL)
00305         cmdlineArgs.push_back(argv[i]); //might be a key which is added by Simulator, we'll come back to it once Simulator has been launched
00306       else if(plist::PrimitiveBase* pbp=dynamic_cast<plist::PrimitiveBase*>(ob)) {
00307         try {
00308           pbp->set(value);
00309         } catch(const XMLLoadSave::bad_format& bf) {
00310           cout << "'" << value << "' is a bad value for '" << key << "'" << endl;
00311           cout << bf.what() << endl;
00312           return false;
00313         } catch(const std::exception& e) {
00314           cout << "An exception occured: " << e.what() << endl;
00315           return false;
00316         }
00317       } else {
00318         cout << "Cannot assign to a dictionary ("<<key<<")" << endl;
00319         return false;
00320       }
00321     } else {
00322       cmdlineArgs.push_back(argv[i]); //command to run in simulator
00323     }
00324   }
00325   
00326   return true;
00327 }
00328   
00329 // these come from VERSION in the root of the framework directory
00330 extern const char * TEKKOTSU_VERSION;
00331 extern const char * LIBTEKKOTSU_DATE;
00332 extern const char * LIBTEKKOTSU_TIME;
00333 
00334 bool sim::run() {
00335   std::cout << "Tekkotsu Robotics Framework " << TEKKOTSU_VERSION;
00336 #ifndef NO_LIBTEKKOTSU
00337   std::cout << ", libtekkotsu compiled on " << LIBTEKKOTSU_DATE << " at " << LIBTEKKOTSU_TIME;
00338 #endif
00339   std::cout << std::endl;
00340   // cout << "To replay: --seed " << srandSeed << "," << zigSeed1 << ',' << zigSeed2 << endl;;
00341   srand(srandSeed);
00342   srandom(srandSeed);
00343   RanNormalSetSeedZig32(&zigSeed1,zigSeed2);
00344   
00345   // point of no return for setting multiprocess mode
00346   cfgCheck.holdMultiprocess();
00347   RCRegion::setMultiprocess(config.multiprocess);
00348   if(!config.multiprocess) {
00349     ProcessID::setIDHooks(getProcessID,setProcessID);
00350     
00351     //Setup wireless
00352     if(wireless==NULL) { // if running single-process, may already be set up
00353       wireless = new Wireless();
00354       sout=wireless->socket(Socket::SOCK_STREAM,Wireless::WIRELESS_DEF_RECV_SIZE,Wireless::WIRELESS_DEF_SEND_SIZE*12);
00355       serr=wireless->socket(Socket::SOCK_STREAM,Wireless::WIRELESS_DEF_RECV_SIZE,Wireless::WIRELESS_DEF_SEND_SIZE*4);
00356       wireless->setDaemon(sout);
00357       wireless->setDaemon(serr);
00358       serr->setFlushType(Socket::FLUSH_BLOCKING);
00359       sout->setTextForward();
00360       serr->setForward(sout);
00361     }
00362     
00363     //Setup Kinematics
00364     if(kine==NULL)
00365       kine=new Kinematics();
00366   }
00367   
00368   if(config.tgtRunlevel!=SharedGlobals::RUNNING)
00369     showRunlevels=true;
00370   
00371   //this will force all of the processes to wait at the end of construction
00372   //until we're done spawning all of them
00373   globals->level_count[SharedGlobals::CREATED]++;
00374     
00375   /*cout << "Spawning processes..." << endl;
00376   cout.setf(ios::left);
00377   cout << "  Initializing runlevel " << setw(12) << SharedGlobals::runlevel_names[SharedGlobals::CONSTRUCTING] << endl;
00378   cout.unsetf(ios::left);*/
00379   if(fork_process<Main>()) ;
00380   else if(fork_process<Motion>()) ;
00381   else if(fork_process<SoundPlay>()) ;
00382   else if(config.multiprocess)
00383     manage_process<Simulator>();
00384   else {
00385     fork_process<Simulator>();
00386     globals->level_count[SharedGlobals::CREATED]--;
00387 #ifdef __APPLE__
00388     // primaries will self-count and the last one will stop the main run loop
00389     // add a no-op source to prevent CFRunLoopRun() from immediately exiting on 10.5
00390     CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
00391     CFRunLoopAddSource(CFRunLoopGetCurrent(), CFRunLoopSourceCreate(NULL,0,&context), kCFRunLoopDefaultMode);
00392     CFRunLoopRun();
00393 #else
00394     // we need to monitor primaries, join each and then fall through
00395     while(primaries.size()>0) {
00396       primaries.back()->join();
00397       delete primaries.back();
00398       primaries.pop_back();
00399     }
00400 #endif
00401   } 
00402   
00403   //every process is going to pass through here on their way out
00404   globals->level_count[SharedGlobals::DESTRUCTED]++;
00405   
00406   return true;
00407 }
00408   
00409 sim::~sim() {
00410 #if TEKKOTSU_SHM_STYLE!=NO_SHM
00411   MutexLockBase::setSemaphoreManager(NULL);
00412 #endif
00413   MessageQueueBase::setSemaphoreManager(NULL);
00414   globals=NULL;
00415   ipc_setup=NULL;
00416   
00417   if(child==static_cast<pid_t>(-1)) // never got to the fork (or had an error at the fork)
00418     return;
00419   
00420   if(child!=0) {
00421     CallbackThread waitChild(&sim::wait_child,*this,true);
00422     FailsafeThread failsafe(waitChild,1.5,true);
00423     if(waitChild.join()==Thread::CANCELLED)
00424       cout << ProcessID::getIDStr() << ": Waiting for child (pid " << child << ") to exit..." << endl;
00425     wait_child();
00426   }
00427 }
00428 
00429 void sim::wait_child() {
00430   int status;
00431   int res=waitpid(child,&status,0);
00432   Thread::testCurrentCancel();
00433   if(res<0 && errno!=ECHILD)
00434     perror("wait");
00435 }
00436   
00437 void sim::wait_runlevel(SharedGlobals::runlevel_t level) {
00438   globals->lock.lock(ProcessID::getID());
00439   globals->level_count[level]++;
00440   if(globals->level_count[level]==1 && showRunlevels) {
00441     cout.setf(ios::left);
00442     cout << "Collecting for runlevel " << setw(12) << SharedGlobals::runlevel_names[level] << "  |=" << flush;
00443     cout.unsetf(ios::left);
00444   }
00445   string nm=Process::getName();
00446   if(showRunlevels)
00447     cout << nm << '=' << flush;
00448   if(globals->level_count[level]==globals->level_count[SharedGlobals::CREATED] && showRunlevels)
00449     cout << "|  done" << endl;
00450   globals->lock.unlock();
00451   while(globals->level_count[level]!=globals->level_count[SharedGlobals::CREATED])
00452     usleep(150*1000);
00453   globals->lock.lock(ProcessID::getID());
00454   globals->lock.unlock();
00455 }
00456   
00457 template<class T>
00458 void sim::manage_process() {
00459   //initialize the first runlevel
00460   globals->lock.lock(T::getID());
00461   globals->level_count[SharedGlobals::CONSTRUCTING]++;
00462 
00463   T t;
00464   ASSERT(T::getID()==ProcessID::getID(),"Process ID set incorrectly!");
00465   
00466   globals->lock.unlock();
00467   while(globals->level_count[SharedGlobals::CONSTRUCTING]!=globals->level_count[SharedGlobals::CREATED])
00468     usleep(150*1000);
00469   globals->lock.lock(ProcessID::getID());
00470   globals->lock.unlock();
00471   
00472   //now just walk through each of the other steps
00473   wait_runlevel(SharedGlobals::STARTING);
00474   t.start();
00475   wait_runlevel(SharedGlobals::RUNNING);
00476   t.run(); //should return if/when SharedGlobals::shutdown flag is set
00477   wait_runlevel(SharedGlobals::STOPPING);
00478   t.stop();
00479   wait_runlevel(SharedGlobals::DESTRUCTING);
00480 }
00481   
00482 template<class T>
00483 bool sim::fork_process() {
00484   if(config.multiprocess) {
00485     RCRegion::aboutToFork(T::getID());
00486     MutexLockBase::aboutToFork();
00487   }
00488   //increment this *before* the fork to guarantee everyone knows to wait for the new process
00489   globals->level_count[SharedGlobals::CREATED]++;
00490   if(config.multiprocess) {
00491     child=fork();
00492     if(child==static_cast<pid_t>(-1)) {
00493       cerr << "Unable to spawn simulator process!" << endl;
00494       exit(1);
00495     }
00496     if(child!=0) {
00497       manage_process<T>();
00498       return true;
00499     }
00500     original=false;
00501   } else {
00502     primaries.push_back(new PrimaryThread<T>());
00503     primaries.back()->start();
00504   }
00505   return false;
00506 }
00507 
00508 ProcessID::ProcessID_t sim::getProcessID() {
00509   Thread* th=Thread::getCurrent();
00510   if(th==NULL)
00511     return ProcessID::SimulatorProcess; // the main thread will fall into the simulator process
00512   return static_cast<ProcessID::ProcessID_t>(reinterpret_cast<size_t>(th->getGroup()));
00513 }
00514 void sim::setProcessID(ProcessID::ProcessID_t id) {
00515   Thread* th=Thread::getCurrent();
00516   ASSERTRET(th!=NULL,"Unable to set process ID for main thread");
00517   th->setGroup(reinterpret_cast<void*>(id));
00518 }
00519 
00520 int sim::getTermFD() {
00521   const char TERM[] = "/dev/tty";
00522   int fd = open(TERM, O_RDWR);
00523   if (fd < 0)
00524     cerr << "Unable to open terminal " << TERM << endl;
00525   //cout << "term fd is " << fd << endl;
00526   return fd;
00527 }
00528 
00529 void sim::handle_signal(int sig) {
00530   /* reset the signal handler for next time */
00531   //  signal(sig, handle_signal);
00532   /* mask any further signals while we're inside the handler. */
00533   sigset_t mask_set;
00534   sigfillset(&mask_set);
00535   sigprocmask(SIG_SETMASK, &mask_set, NULL);
00536   
00537   const char * name=NULL;
00538   char defBuf[30];
00539   switch(sig) {
00540   case SIGINT: name="SIGINT"; break;
00541   case SIGQUIT: name="SIGQUIT"; break;
00542   case SIGBUS: name="SIGBUS"; break;
00543   case SIGSEGV: name="SIGSEGV"; break;
00544   case SIGTERM: name="SIGTERM"; break;
00545   case SIGABRT: name="SIGABRT"; break;
00546   case SIGFPE: name="SIGFPE"; break;
00547   case SIGPIPE: name="SIGPIPE"; break;
00548   case SIGHUP: name="SIGHUP"; break;
00549   default:
00550     name=defBuf;
00551     snprintf(defBuf,30,"signal %d",sig);
00552     break;
00553   }
00554   cout << "*** ERROR " << Process::getName() << ": Received " << name << endl;
00555   
00556   static bool firstCall=true;
00557   static bool inBackTrace=false;
00558   if(!firstCall && !inBackTrace) {
00559     cerr << "Signal handler was recursively called, may be leaked IPC resources :(" << endl;
00560     cerr << "The 'ipcs' tool can be used to manually free these, if it becomes a problem. " << endl;
00561     cerr << "However, simply re-running will generally reclaim the previous buffers for you." << endl;
00562     _exit(EXIT_FAILURE);
00563     return;
00564   }
00565   firstCall=false;
00566   
00567 #ifndef DISABLE_READLINE
00568   // this blocks until a character is pressed on Mac:
00569   //rl_reset_terminal(NULL);
00570   
00571   // so does this:
00572   //if(sig==SIGINT)
00573   //  rl_free_line_state();
00574   //rl_cleanup_after_signal();
00575 
00576   // sigh... if you want something done right...
00577   int fd = getTermFD();
00578   if(fd<0 || tcsetattr(fd, TCSADRAIN, &ttyMode) == -1)
00579     cerr << "tcsetattr failed, could not restore terminal state" << endl;
00580   close(fd);
00581 #endif
00582   
00583   if(sig!=SIGINT && sig!=SIGTERM && !inBackTrace) {
00584     inBackTrace=true;
00585     stacktrace::displayCurrentStackTrace(25);
00586     inBackTrace=false;
00587   }
00588   
00589   cout << "*** ERROR " << Process::getName() << ": Engaging fault shutdown..." << endl;
00590   badExitCleanup();
00591   _exit(EXIT_FAILURE);
00592 }
00593   
00594 void sim::handle_exit() {
00595   static bool firstCall=true;
00596   if(!firstCall) {
00597     cerr << "handle_exit was recursively called" << endl;
00598     return;
00599   }
00600   firstCall=false;
00601 #ifndef DISABLE_READLINE
00602   rl_reset_terminal(NULL);
00603 
00604   // sigh... if you want something done right...
00605   int fd = getTermFD();
00606   if(fd<0 || tcsetattr(fd, TCSADRAIN, &ttyMode) == -1)
00607     cerr << "tcsetattr failed, could not restore terminal state" << endl;
00608   close(fd);
00609 #endif
00610   
00611   if(RCRegion::NumberOfAttach()==0) {
00612     /*if(original)
00613      cout << "Clean shutdown complete.  Have a nice day." << endl;*/
00614     return;
00615   }
00616   cout << "*** ERROR " << Process::getName() << ": Exit with attached memory regions, engaging fault shutdown..." << endl;
00617   badExitCleanup();
00618 }
00619 
00620 void sim::badExitCleanup() {
00621   if(globals!=NULL && !globals->hadFault()) {
00622     if(!MessageQueueBase::getSemaphoreManager()->hadFault())
00623       globals->lock.lock(ProcessID::getID());
00624     if(globals->level_count[SharedGlobals::CREATED]>0)
00625       globals->level_count[SharedGlobals::CREATED]--;
00626     else
00627       cout << "*** ERROR " << Process::getName() << ": level_count[CREATED] underflow" << endl;
00628     globals->signalShutdown();
00629     if(!MessageQueueBase::getSemaphoreManager()->hadFault())
00630       globals->lock.unlock();
00631     globals->faultShutdown();
00632   } else {
00633     cerr << "*** ERROR " << Process::getName() << ": exit with previous global fault" << endl;
00634   }
00635   
00636   if(MessageQueueBase::getSemaphoreManager()!=NULL && !MessageQueueBase::getSemaphoreManager()->hadFault()) {
00637     cout << "*** ERROR " << Process::getName() << ": Dereferencing message queue SemaphoreManager" << endl;
00638     MessageQueueBase::getSemaphoreManager()->faultShutdown();
00639     MessageQueueBase::setSemaphoreManager(NULL);
00640 #if TEKKOTSU_SHM_STYLE!=NO_SHM
00641     MutexLockBase::setSemaphoreManager(NULL);
00642 #endif
00643   } else {
00644     cerr << "*** ERROR " << Process::getName() << ": exit with previous SemaphoreManager fault" << endl;
00645   }
00646   
00647 #if TEKKOTSU_SHM_STYLE!=NO_SHM
00648   if(MutexLockBase::getSemaphoreManager()==NULL) {
00649     cerr << "*** ERROR " << Process::getName() << ": Mutex semaphore manager is NULL? (should have reset to internal manager)" << endl;
00650   } else if(!MutexLockBase::getSemaphoreManager()->hadFault()) {
00651     cout << "*** ERROR " << Process::getName() << ": Dereferencing mutex SemaphoreManager" << endl;
00652     MutexLockBase::getSemaphoreManager()->faultShutdown();
00653     MutexLockBase::setSemaphoreManager(NULL); // switches to internal preallocated semaphore manager (if not already)
00654     MutexLockBase::getSemaphoreManager()->faultShutdown(); // release that too.
00655   } else {
00656     cerr << "*** ERROR " << Process::getName() << ": exit with previous SemaphoreManager fault" << endl;
00657   }
00658   
00659   cout << "*** ERROR " << Process::getName() << ": Dereferencing shared memory regions" << endl;
00660   RCRegion::faultShutdown();
00661 #endif
00662   
00663   cout << "*** ERROR " << Process::getName() << ": Exiting..." << endl;
00664 }
00665   
00666 unsigned int sim::measure_usleep_cost() {
00667   usleep(50000); //to hopefully clear out the scheduler for the duration of our test
00668   const unsigned int TRIALS=50;
00669   TimeET mintime(1.0); //should definitely be less than a second
00670   for(unsigned int i=0; i<TRIALS; i++) {
00671     //measure usleep (plus overhead)
00672     TimeET cur;
00673     usleep(1); // at least 1 to avoid being a no-op
00674     TimeET elapsed(cur.Age());
00675     if(elapsed<mintime)
00676       mintime=elapsed;
00677   }
00678   usleep(50000); //to hopefully clear out the scheduler for the duration of our test
00679   TimeET minover(1.0); //should definitely be less than a second
00680   for(unsigned int i=0; i<TRIALS; i++) {
00681     //measure overhead
00682     TimeET cur;
00683     TimeET elapsed(cur.Age());
00684     if(elapsed<minover)
00685       minover=elapsed;
00686   }
00687   if(mintime<minover) { // something went wrong -- overhead is greater than total
00688     mintime=0L;
00689     //cout << "usleep granularity couldn't be measured, default to 10ms" << endl;
00690   } else {
00691     //subtract overhead
00692     mintime-=minover;
00693     //cout << "usleep granularity is " << mintime.Value()*1.0e6 << "us";
00694     if(mintime<2L) {
00695       mintime=2L;
00696       //cout << ", reset to 2ms";
00697     }
00698     //cout << endl;
00699   }
00700   return static_cast<unsigned>(mintime.Value()*1.0e6);
00701 }
00702 
00703 sim::ConfigErrorCheck::ConfigErrorCheck() : PrimitiveListener(), holdMPValue(config.multiprocess) {}
00704 
00705 sim::ConfigErrorCheck::~ConfigErrorCheck() {
00706   sim::config.multiprocess.removePrimitiveListener(this);
00707 }
00708 
00709 void sim::ConfigErrorCheck::plistValueChanged(const plist::PrimitiveBase& pl) {
00710   if(&pl==&sim::config.multiprocess) {
00711 #if TEKKOTSU_SHM_STYLE==NO_SHM
00712     if(sim::config.multiprocess) {
00713       cerr << "ERROR: TEKKOTSU_SHM_STYLE was set to NO_SHM during compile, Muliprocess cannot be set to 'true'" << endl;
00714       sim::config.multiprocess=false;
00715     }
00716 #else
00717 #  ifdef DEBUG
00718     cerr << "sim::config.Multiprocess set to " << sim::config.multiprocess << endl;
00719 #  endif
00720     if(holdMPValue!=sim::config.multiprocess) {
00721       cerr << "ERROR: Cannot change sim::config.Multiprocess during execution, must set from command line or load from settings file" << endl;
00722       sim::config.multiprocess=holdMPValue;
00723     }
00724 #endif
00725   }
00726 }
00727 
00728 void sim::ConfigErrorCheck::holdMultiprocess() {
00729   holdMPValue=sim::config.multiprocess;
00730   sim::config.multiprocess.addPrimitiveListener(this);
00731 }

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Mon May 9 05:01:39 2016 by Doxygen 1.6.3