cgma
|
00001 //------------------------------------------------------------------------- 00002 // Filename : AppUtil.cc 00003 // 00004 // Purpose : This file represents the Cubit application itself. 00005 // 00006 // Special Notes : 00007 // 00008 // Creator : Darryl Melander 00009 // 00010 // Date : 06/08/98 00011 // 00012 // Owner : Darryl Melander 00013 //------------------------------------------------------------------------- 00014 00015 #include <cstdio> 00016 #include <cstdlib> 00017 #include <signal.h> 00018 #ifndef _WIN32 00019 # include <unistd.h> 00020 # include <termios.h> 00021 # include <sys/ioctl.h> 00022 #endif 00023 #include <ctype.h> 00024 #include <time.h> 00025 00026 #include "AppUtil.hpp" 00027 #include "CubitMessage.hpp" 00028 #include "CubitString.hpp" 00029 #include "CubitObserver.hpp" 00030 #include "SettingHandler.hpp" 00031 #include "StubProgressTool.hpp" 00032 00033 #ifdef BUILD_WITH_CONCURRENT_SUPPORT 00034 #include "CubitQtConcurrentApi.h" 00035 #endif 00036 00037 #ifdef CUBIT_USE_THREADS 00038 #include "CubitThreadPool.hpp" 00039 #endif 00040 00041 // Different platforms follow different conventions 00042 #ifndef _WIN32 00043 #include <sys/resource.h> 00044 #include <sys/types.h> 00045 #include <sys/stat.h> 00046 #include <fcntl.h> 00047 #endif 00048 #ifdef SOLARIS 00049 #include <procfs.h> 00050 #include <sys/syscall.h> 00051 extern "C" int getrusage(int, struct rusage *); 00052 #ifndef RUSAGE_SELF 00053 #include </usr/ucbinclude/sys/rusage.h> 00054 #endif 00055 #endif 00056 00057 #ifdef HP 00058 #include <sys/syscall.h> 00059 #if 0 00060 #if OS_VERSION != 1020 00061 #define getrusage(a, b) syscall(SYS_GETRUSAGE, a, b) 00062 #endif 00063 #endif 00064 #endif 00065 00066 #ifndef PATH_MAX 00067 #define PATH_MAX _MAX_PATH 00068 #endif 00069 00070 #ifdef _WIN32 00071 #include <windows.h> 00072 extern "C" IMAGE_DOS_HEADER __ImageBase; 00073 #else 00074 #include <dlfcn.h> 00075 #endif 00076 00077 AppUtil* AppUtil::instance_ = NULL; 00078 00079 #ifdef HP 00080 #ifndef UNIX_PORT 00081 extern "C" int Error; 00082 #else 00083 int Error = 0; 00084 #endif //unix_port 00085 #endif 00086 #ifdef _WIN32 00087 # include <direct.h> 00088 # include <windows.h> 00089 //# define strdup _strdup 00090 #endif 00091 #include "CubitUtil.hpp" 00092 00093 // Interrupt handling defintions. 00094 /* Setting this flag (asynchronously) to CUBIT_TRUE 00095 * will cause Cubit/CGM to attempt to abort any current 00096 * operations and return. 00097 * 00098 * NOTE: IT IS THE RESPONSIBILITY OF THE APPLICATION 00099 * USING CGM TO RESET THIS FLAG TO CUBIT_FALSE!!! 00100 * For Cubit, this flag is reset in UserInterface. 00101 * 00102 * CubitApp provides a default signal hander that will 00103 * set this flag to CUBIT_TRUE whenever a SIGINT (^C) 00104 * is detected. It is still the responsibility of the 00105 * application using CGM to reset the flag to false! 00106 * The signal hander provided by CubitApp may be set by 00107 * calling CubitApp::instance()->catch_interrupt(CUBIT_TRUE). 00108 * 00109 * The storage space for this flag is defined in CubitApp.cpp 00110 * and initialized in CubitApp::initialize(). 00111 */ 00112 static volatile CubitBoolean cubit_intr = CUBIT_FALSE; 00113 00114 extern "C" void cubit_update_terminal_size(int) 00115 { 00116 #if defined(_WIN32) 00117 00118 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/getconsolescreenbufferinfo.asp 00119 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/scrolling_a_screen_buffer_s_window.asp 00120 HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); 00121 CONSOLE_SCREEN_BUFFER_INFO scr_info; 00122 if (GetConsoleScreenBufferInfo(h_stdout, &scr_info)) 00123 { 00124 AppUtil::instance()->set_terminal_size( 00125 // dwSize.Y is will return the height of the scroll-back buffer. 00126 // srWindow contains the position of the visible rect of the 00127 // window in the buffer. That is the height of the window. 00128 scr_info.srWindow.Bottom - scr_info.srWindow.Top, 00129 // use width of scroll buffer rather than size of window 00130 // for number of columns. The window size is useless as 00131 // caller would need to know the offset in the X direction 00132 // to make use of it and we aren't returning that. Besides, 00133 // the user presumably set the scroll buffer to this width 00134 // because that's the line length (s)he wants. 00135 scr_info.dwSize.X 00136 ); 00137 } 00138 00139 #elif defined(TIOCGWINSZ) 00140 00141 // On UNIX platforms, register this function as a handler for 00142 // SIGWINCH. The system will then call this function any time 00143 // the user changes the size of the terminal window. 00144 #ifdef SIGWINCH 00145 signal( SIGWINCH, &cubit_update_terminal_size ); 00146 #endif 00147 00148 const int file = fileno(stdout); 00149 struct winsize size; 00150 if( ioctl( file, TIOCGWINSZ, (char*)&size ) == 0 ) 00151 { 00152 AppUtil::instance()->set_terminal_size( size.ws_row, size.ws_col ); 00153 } 00154 00155 #endif 00156 } 00157 00158 00159 CubitBoolean AppUtil::catching_sigint_ = CUBIT_FALSE; 00160 00161 AppUtil* AppUtil::instance_no_create() 00162 { 00163 return instance_; 00164 } 00165 00166 // Returns the singleton instance of the app object. 00167 // If it is being created for the first time, it 00168 // does application initialization. 00169 AppUtil* AppUtil::instance() 00170 { 00171 if (instance_ == NULL) 00172 { 00173 instance_ = new AppUtil(); 00174 00175 if ( !instance_ ) 00176 { 00177 PRINT_ERROR(" *** Unable to initialize application ***\n"); 00178 exit(1); 00179 } 00180 00181 // Initialize interrupted flag to false. 00182 cubit_intr = CUBIT_FALSE; 00183 00184 cubit_update_terminal_size(0); 00185 00186 initialize_settings(); 00187 } 00188 return instance_; 00189 } 00190 00191 AppUtil::AppUtil() 00192 { 00193 term_width = 0; 00194 term_height = 0; 00195 mAppStarted = CUBIT_FALSE; 00196 00197 // reset catching_sigint_ here, even though it's static, 'cuz it used 00198 // to be reset to false on construction before going static 00199 00200 catching_sigint_ = CUBIT_FALSE; 00201 00202 // create a default progress tool that does nothing 00203 // an app can replace this with an app specific progress tool 00204 mProgressTool = new StubProgressTool(); 00205 00206 concurrentSupport = false; 00207 00208 this->mInterruptThrottle = clock(); 00209 00210 #ifdef CUBIT_USE_THREADS 00211 mThreadPool = new CubitThreadPool; 00212 #else 00213 mThreadPool = NULL; 00214 #endif 00215 } 00216 00217 AppUtil::~AppUtil() 00218 { 00219 instance_ = NULL; 00220 catch_interrupt( CUBIT_FALSE ); 00221 if (mProgressTool) 00222 delete mProgressTool; 00223 mProgressTool = NULL; 00224 #ifdef CUBIT_USE_THREADS 00225 delete mThreadPool; 00226 #endif 00227 } 00228 00229 void AppUtil::delete_instance() 00230 { 00231 if( NULL != instance_ ) 00232 { 00233 delete instance_; 00234 instance_ = NULL; 00235 } 00236 } 00237 00238 #ifdef CAT 00239 // Returns how many days until the app expires. A negative 00240 // number inicates it is already expired. If there is 00241 // an extension, has_extension is set to CUBIT_TRUE. 00242 int AppUtil::days_to_expire(int year, int month, int day ) const 00243 { 00244 // Setup variables and constants 00245 struct tm expiration_time; 00246 time_t raw_expire; 00247 time_t raw_current; 00248 int days; 00249 const int SECONDS_PER_DAY = 24 * 60 * 60; 00250 00251 // Get the current time, raw 00252 time(&raw_current); 00253 00254 // Setup a dissected expiration date 00255 expiration_time.tm_year = year - 1900; 00256 expiration_time.tm_mon = month - 1; 00257 expiration_time.tm_mday = day; 00258 expiration_time.tm_hour = 0; 00259 expiration_time.tm_min = 0; 00260 expiration_time.tm_sec = 0; 00261 expiration_time.tm_isdst = 0; 00262 00263 // Convert expiration date from dissected to raw 00264 raw_expire = mktime(&expiration_time); 00265 // Get difference between dates 00266 days = (int)difftime(raw_expire, raw_current) / SECONDS_PER_DAY; 00267 //days = INT_MAX;//(int)difftime(raw_expire, raw_current) / SECONDS_PER_DAY; 00268 //changed back by aga@cat|11/2/04 00269 00270 // Return days left 00271 return days; 00272 } 00273 #endif 00274 00275 // Prints out information about the session, like 00276 // execution time and memory problems. 00277 void AppUtil::report_resource_usage() const 00278 { 00279 00280 #ifndef JANUS 00281 #ifndef _WIN32 00282 struct rusage r_usage; 00283 float utime, stime; 00284 apputil_getrusage(r_usage); 00285 utime = (float)r_usage.ru_utime.tv_sec + 00286 ((float)r_usage.ru_utime.tv_usec/1.e6); 00287 stime = (float)r_usage.ru_stime.tv_sec + 00288 ((float)r_usage.ru_stime.tv_usec/1.e6); 00289 static int pagesize = getpagesize(); 00290 PRINT_INFO("\nEnd of Execution\n"); 00291 PRINT_INFO("User time = %f\n", utime); 00292 PRINT_INFO("System time = %f\n", stime); 00293 PRINT_INFO("Total time = %f\n", utime+stime); 00294 PRINT_INFO("Max resident set size = %ld bytes\n", r_usage.ru_maxrss*pagesize); 00295 PRINT_INFO("Int resident set size = %ld\n", r_usage.ru_idrss); 00296 PRINT_INFO("Minor Page Faults = %ld\n", r_usage.ru_minflt); 00297 PRINT_INFO("Major Page Faults = %ld\n", r_usage.ru_majflt); 00298 PRINT_INFO("Swaps = %ld\n", r_usage.ru_nswap); 00299 #endif// _WIN32 00300 #endif//JANUS 00301 } 00302 00303 00304 00305 // Default handler for SIGINT provided by AppUtil. 00306 extern "C" void sigint_handler(int) 00307 { 00308 #ifndef CUBIT_NO_SIGNAL 00309 if( signal( SIGINT, sigint_handler ) == SIG_ERR ) 00310 PRINT_ERROR("Cannot continue to catch SIGINT!\n"); 00311 cubit_intr = CUBIT_TRUE; 00312 #endif 00313 } 00314 00315 // Set signal handler for SIGINT. 00316 void AppUtil::catch_interrupt( CubitBoolean yesno ) 00317 { 00318 #ifndef CUBIT_NO_SIGNAL 00319 if( yesno ) 00320 { 00321 if( signal( SIGINT, sigint_handler ) == SIG_ERR ) 00322 PRINT_ERROR("Can't catch SIGINT!\n"); 00323 else 00324 catching_sigint_ = CUBIT_TRUE; 00325 } 00326 else 00327 { 00328 if( signal( SIGINT, SIG_DFL ) == SIG_ERR ) 00329 PRINT_ERROR("Can't reset SIGINT handler!\n"); 00330 else 00331 catching_sigint_ = CUBIT_FALSE; 00332 } 00333 #endif 00334 } 00335 00336 00337 void AppUtil::startup() 00338 { 00339 #ifndef NOT_AN_APP 00340 if (mAppStarted) 00341 return; 00342 00343 // get paths to binaries that AppUtil is compiled into 00344 // we'll make this our "cubitDir" and can be used a reference for 00345 // where Cubit is installed 00346 #ifdef _WIN32 00347 wchar_t path_buffer[PATH_MAX]; 00348 if(GetModuleFileNameW((HINSTANCE)&__ImageBase, path_buffer, PATH_MAX)) 00349 { 00350 *wcsrchr(path_buffer, '\\') = '\0'; 00351 cubitDir = CubitString::toUtf8(path_buffer); 00352 } 00353 #else 00354 Dl_info dl_info; 00355 00356 // Note: this is a nasty hack to avoid the warning: 00357 // ISO C++ forbids casting between pointer-to-function and pointer-to-object 00358 // Even with reinterpret_cast, this warning is still generated 00359 // GCC accepts reinterpret_cast for purposes like in dlsym calls as an extension, 00360 // but that might not work for other compilers 00361 // This union-trick has been applied to some open source code, like: 00362 // https://mail.gnome.org/archives/commits-list/2011-February/msg06409.html 00363 // http://sourceforge.net/p/scstudio/mailman/message/29083334/ 00364 union { 00365 void* ptr; 00366 AppUtil* (*func)(); 00367 } cast_u; 00368 cast_u.func = AppUtil::instance; 00369 00370 dladdr(cast_u.ptr, &dl_info); 00371 CubitString path_buffer = dl_info.dli_fname; 00372 size_t idx = path_buffer.find_last('/'); 00373 if(idx != CubitString::npos) 00374 { 00375 path_buffer = path_buffer.substr(0, idx); 00376 } 00377 cubitDir = path_buffer; 00378 #endif 00379 00380 #ifdef BUILD_WITH_CONCURRENT_SUPPORT 00381 mConcurrentInstance = new CubitQtConcurrent; 00382 this->concurrentSupport = true; 00383 #endif 00384 00385 mAppStarted = CUBIT_TRUE; 00386 // add startup code here 00387 // nothing to do for now 00388 #endif /*NOT_AN_APP*/ 00389 return; 00390 } 00391 00392 int AppUtil::shutdown() 00393 { 00394 // When the user is done, print out additional info if requested 00395 if (DEBUG_FLAG(3)) 00396 report_resource_usage(); 00397 00398 // Return the number of errors in the session 00399 int ret_val = ( CubitMessage::instance()->error_count() ); 00400 if ( ret_val > 0 ) 00401 { 00402 PRINT_ERROR("Errors found during CUBIT session.\n"); 00403 } 00404 00405 // Delete the singletons 00406 CubitMessage::delete_instance(); 00407 00408 SettingHandler::delete_instance(); 00409 00410 #ifdef BUILD_WITH_CONCURRENT_SUPPORT 00411 delete mConcurrentInstance; 00412 #endif 00413 00414 mAppStarted = CUBIT_FALSE; 00415 00416 return ret_val; 00417 } 00418 void AppUtil::apputil_getrusage(struct rusage &r_usage) const 00419 { 00420 // get the resource usage as defined by getrusage 00421 #ifndef JANUS 00422 #ifndef _WIN32 00423 getrusage(RUSAGE_SELF, &r_usage); 00424 00425 if (r_usage.ru_maxrss == 0) { 00426 // this machine doesn't return rss - try going to /proc 00427 // print the file name to open 00428 #ifdef SOLARIS 00429 char buffer[120]; 00430 strcpy(buffer, "/proc/self/psinfo"); 00431 int file_ptr = -1; 00432 file_ptr = open(buffer, O_RDONLY); 00433 if (file_ptr < 0) return; 00434 struct psinfo myps_info; 00435 read(file_ptr, &myps_info, sizeof(myps_info)); 00436 static int page_size = getpagesize(); 00437 r_usage.ru_maxrss = myps_info.pr_rssize*1024/page_size; 00438 r_usage.ru_idrss = myps_info.pr_size*1024/page_size; 00439 close (file_ptr); 00440 #endif // SOLARIS 00441 #ifdef CUBIT_LINUX 00442 char file_str[4096], dum_str[4096]; 00443 int file_ptr = -1, file_len; 00444 file_ptr = open("/proc/self/stat", O_RDONLY); 00445 file_len = read(file_ptr, file_str, sizeof(file_str)-1); 00446 if (file_len == 0) return; 00447 close(file_ptr); 00448 file_str[file_len] = '\0'; 00449 // read the preceeding fields and the ones we really want... 00450 int dum_int; 00451 unsigned int dum_uint, vm_size, rss; 00452 static int page_size = getpagesize(); 00453 int num_fields = sscanf(file_str, 00454 "%d " // pid 00455 "%s " // comm 00456 "%c " // state 00457 "%d %d %d %d %d " // ppid, pgrp, session, tty, tpgid 00458 "%u %u %u %u %u " // flags, minflt, cminflt, majflt, cmajflt 00459 "%d %d %d %d %d %d " // utime, stime, cutime, cstime, counter, priority 00460 "%u %u " // timeout, itrealvalue 00461 "%d " // starttime 00462 "%u %u", // vsize, rss 00463 &dum_int, 00464 dum_str, 00465 dum_str, 00466 &dum_int, &dum_int, &dum_int, &dum_int, &dum_int, 00467 &dum_uint, &dum_uint, &dum_uint, &dum_uint, &dum_uint, 00468 &dum_int, &dum_int, &dum_int, &dum_int, &dum_int, &dum_int, 00469 &dum_uint, &dum_uint, 00470 &dum_int, 00471 &vm_size, &rss); 00472 if (num_fields == 24) { 00473 r_usage.ru_maxrss = rss/page_size; 00474 r_usage.ru_idrss = vm_size/page_size; 00475 } 00476 #endif // CUBIT_LINUX 00477 } 00478 #endif // _WIN32 00479 #endif // JANUS 00480 } 00481 00482 bool AppUtil::get_terminal_size( int& rows, int& cols ) 00483 { 00484 cols = term_width; 00485 rows = term_height; 00486 return rows || cols; 00487 } 00488 00489 void AppUtil::set_terminal_size( int rows, int cols ) 00490 { 00491 term_width = cols; 00492 term_height = rows; 00493 } 00494 00495 ProgressTool *AppUtil::progress_tool() 00496 { 00497 assert(mProgressTool != NULL); 00498 00499 return mProgressTool; 00500 } 00501 00502 void AppUtil::progress_tool(ProgressTool* pTool) 00503 { 00504 assert(pTool != NULL); // existence is assumed elsewhere in the code 00505 if (pTool) 00506 { 00507 if (mProgressTool) 00508 { 00509 delete mProgressTool; 00510 } 00511 mProgressTool = pTool; 00512 } 00513 } 00514 00515 CubitThreadPool* AppUtil::thread_pool() 00516 { 00517 return mThreadPool; 00518 } 00519 00520 void AppUtil::initialize_settings() 00521 { 00522 CubitMessage::initialize_settings(); 00523 } 00524 00525 CubitBoolean AppUtil::interrupt() 00526 { 00527 if(this->mProgressTool) 00528 { 00529 clock_t new_clock = clock(); 00530 clock_t diff = new_clock - this->mInterruptThrottle; 00531 if((diff / static_cast<double>(CLOCKS_PER_SEC)) > 0.1) 00532 { 00533 this->mInterruptThrottle = new_clock; 00534 mProgressTool->check_interrupt(); 00535 } 00536 } 00537 00538 return cubit_intr; 00539 } 00540 00541 void AppUtil::set_interrupt(CubitBoolean f) 00542 { 00543 if(f) 00544 { 00545 // call raise so it works regardless of the current signal handler 00546 raise(SIGINT); 00547 } 00548 cubit_intr = f; 00549 } 00550 00551 void AppUtil::clear_interrupt() 00552 { 00553 cubit_intr = CUBIT_FALSE; 00554 } 00555 00556 void AppUtil::send_event(CubitObservable* observable, const CubitEvent& event) 00557 { 00558 this->mEventDispatcher.send_event(observable, event); 00559 }