From 37a49c9b09900c5c609a9a4c1dfa1f19e7ca25dd Mon Sep 17 00:00:00 2001 From: Bruce Allen Date: Wed, 19 Jan 2005 15:54:04 +0000 Subject: [PATCH] Patches from Reinhard Prix to fix several problems. (1) apps being suspended would call boinc_finish(nonzero) and core client would treat this as app failure. Introduce a new function boinc_exit() and call that instead. (2) improvements to GUI build with wxWidgets (3) improved X event handling loop now ensures that X window is taken away when window killed. svn path=/trunk/boinc/; revision=5142 --- api/boinc_api.C | 81 +++++++++++++++++++---------------- api/boinc_api.h | 1 + api/x_opengl.C | 99 +++++++++++++++++++++++-------------------- checkin_notes | 20 +++++++++ clientgui/Makefile.am | 2 +- configure.ac | 22 +++------- lib/filesys.C | 54 +++++++++++------------ 7 files changed, 152 insertions(+), 127 deletions(-) diff --git a/api/boinc_api.C b/api/boinc_api.C index 1011991400..1d71a1d6d3 100644 --- a/api/boinc_api.C +++ b/api/boinc_api.C @@ -72,7 +72,6 @@ static double time_until_fraction_done_update; static double fraction_done; static double last_checkpoint_cpu_time; static bool ready_to_checkpoint = false; -static bool time_to_quit = false; static double last_wu_cpu_time; static bool standalone = false; static double initial_wu_cpu_time; @@ -138,7 +137,7 @@ int boinc_init_options_general(BOINC_OPTIONS& opt) { } if (retval) { fprintf(stderr, "Can't acquire lockfile - exiting\n"); - boinc_finish(0); // not un-recoverable ==> status=0 + boinc_exit(0); // not un-recoverable ==> status=0 } } @@ -162,7 +161,7 @@ int boinc_init_options_general(BOINC_OPTIONS& opt) { fprintf(stderr, "Core client has wrong major version: wanted %d, got %d\n", BOINC_MAJOR_VERSION, aid.core_version/100 ); - boinc_finish(ERR_MAJOR_VERSION); // un-recoverable==> exit with nonzero status + boinc_exit(ERR_MAJOR_VERSION); // un-recoverable==> exit with nonzero status } retval = setup_shared_mem(); if (retval) { @@ -205,14 +204,10 @@ static void send_trickle_up_msg() { // NOTE: a non-zero status tells a running client that we're exiting with -// and "unrecoverable error", which will be reported back to server. -// Therefore, on "recoverable errors", use exit-status == 0 ! +// an "unrecoverable error", which will be reported back to server. +// A zero exit-status will tell the client we've successfully finished the result. int boinc_finish(int status) { - // remove the lockfile - if ( boinc_delete_file (LOCKFILE) != 0) - perror ("boinc_finish(): failed to remove lockfile"); - if (options.send_status_msgs) { boinc_calling_thread_cpu_time(last_checkpoint_cpu_time); last_checkpoint_cpu_time += aid.wu_cpu_time; @@ -235,27 +230,45 @@ int boinc_finish(int status) { boinc_write_init_data_file(); } + // now remove lockfile+exit + boinc_exit(status); + + return(0); // doh... we never get here +} // boinc_finish() + + +// exit a boinc-app +// this simply closes, then removes the app's lockfile and +// calls the appropriate exit-function +#if (!defined _WIN32) && (!defined HANDLE) +typedef int HANDLE; +#endif +extern HANDLE app_lockfile_handle; + +void +boinc_exit (int status) +{ #ifdef _WIN32 - exit(status); + if ( !CloseHandle ( app_lockfile_handle ) ) + perror ( "Failed to close the application-lockfile."); #else - // on Linux < 2.6, probably due to non-POSIX LinuxThreads, _Exit() fails to - // shut down the graphics-thread properly, while exit() does the job and does _NOT_ - // seem to get tangled in exit-atexit loops... -#ifdef __linux__ - exit(status); -#else - // on Mac (and supposedly other systems with POSIX-complian thread-implementations?), - // calling exit() can lead to infinite exit-atexit loops, while _Exit() seems to behave nicely - // This is not pretty but unless someone finds a cleaner solution, we handle the two cases - // separately based on these observations - _Exit(status); + if ( close ( app_lockfile_handle ) ) + perror ( "Failed to close the application-lockfile " LOCKFILE); #endif + // remove the lockfile + if ( boinc_delete_file (LOCKFILE) != 0) + perror ("boinc_finish(): failed to remove lockfile"); + + // on Mac, calling exit() can lead to infinite exit-atexit loops, while _exit() seems + // to behave nicely. This is not pretty but unless someone finds a cleaner solution, + // we handle the Mac-case separately . +#ifdef __APPLE_CC__ + _exit(status); +#else + exit(status); #endif - fprintf(stderr, "..exit() or _Exit() returned... this is totally weird!!\n"); - return 0; -} - +} // boinc_exit() bool boinc_is_standalone() { return standalone; @@ -427,7 +440,7 @@ static void handle_process_control_msg() { break; } if (match_tag(buf, "")) { - boinc_finish(0); // NOTE: exit-status = 0 ! + boinc_exit(0); // NOTE: exit-status = 0 ==> recoverable exit! } } boinc_sleep(1.0); @@ -449,7 +462,7 @@ static void handle_process_control_msg() { if (match_tag(buf, "")) { boinc_status.quit_request = true; if (options.direct_process_action) { - boinc_finish(0); // NOTE: exit-status == 0! + boinc_exit(0); // NOTE: exit-status == 0! } } } @@ -494,9 +507,9 @@ static void worker_timer(int a) { now - (heartbeat_giveup_time - HEARTBEAT_GIVEUP_PERIOD) ); if (options.direct_process_action) { - boinc_finish(0); // NOTE: exit-status == 0! (recoverable error) + boinc_exit(0); // NOTE: exit-status == 0! (recoverable error) } else { - boinc_status.no_heartbeat = true; + boinc_status.no_heartbeat = true; } } } @@ -602,8 +615,8 @@ int boinc_time_to_checkpoint() { // If the application has received a quit request it should checkpoint // - if (time_to_quit || ready_to_checkpoint) { - return 1; + if (ready_to_checkpoint) { + return 1; } return 0; @@ -618,12 +631,6 @@ int boinc_checkpoint_completed() { ready_to_checkpoint = false; time_until_checkpoint = aid.checkpoint_period; - // If it's time to quit, call boinc_finish which will exit the app properly - // - if (time_to_quit) { - fprintf(stderr, "Received quit request from core client\n"); - boinc_finish(ERR_QUIT_REQUEST); - } return 0; } diff --git a/api/boinc_api.h b/api/boinc_api.h index 6beff4063d..3171e3f2a1 100755 --- a/api/boinc_api.h +++ b/api/boinc_api.h @@ -65,6 +65,7 @@ struct BOINC_STATUS { extern int boinc_init(void); extern int boinc_finish(int status); +extern void boinc_exit (int status); extern int boinc_resolve_filename(const char*, char*, int len); extern int boinc_parse_init_data_file(void); extern int boinc_write_init_data_file(void); diff --git a/api/x_opengl.C b/api/x_opengl.C index 4aca40087e..2a9af16f70 100644 --- a/api/x_opengl.C +++ b/api/x_opengl.C @@ -36,6 +36,15 @@ static APP_INIT_DATA aid; extern pthread_t graphics_thread; // thread info extern pthread_t worker_thread; +enum +{ + JUMP_NONE = 0, + JUMP_EXIT, + JUMP_ABORT, + JUMP_LAST +}; // possible longjmp-values to signal from where we jumped: +// 1= exit caught by atexit, 2 = signal caught by handler + void app_debug_msg (char *fmt, ...); @@ -155,15 +164,19 @@ void KillWindow() { void set_mode(int mode) { if (mode == current_graphics_mode) return; + app_debug_msg("set_mode(%d): current_mode = %d. Calling KillWindow(): win = %d\n", mode, current_graphics_mode, win); KillWindow(); app_debug_msg("...KillWindow() survived.\n"); - current_graphics_mode = mode; + if (mode != MODE_HIDE_GRAPHICS) { app_debug_msg("set_mode(): Calling make_new_window(%d)\n", mode); make_new_window(mode); app_debug_msg("...make_new_window() survived.\n"); } + + current_graphics_mode = mode; + return; } // set_mode() @@ -231,10 +244,10 @@ void restart() { app_debug_msg ("exit() called from graphics-thread.\n"); - // we're standalone, a glut-window was open: user must have pressed 'close', so we exit - if (boinc_is_standalone() && glut_is_initialized && win) + // if we are standalone and glut was initialized, we assume user pressed 'close', and we exit the app + if ( boinc_is_standalone() && glut_is_initialized ) { - app_debug_msg("Open glut-window found... means we're exiting now.\n"); + app_debug_msg("Assuming user pressed 'close'... means we're exiting now.\n"); if ( boinc_delete_file(LOCKFILE) != 0) perror ("Failed to remove lockfile..\n"); return; @@ -244,7 +257,7 @@ void restart() atexit(restart); // jump back to entry-point in xwin_graphics_event_loop(); app_debug_msg( "Jumping back to event_loop.\n"); - longjmp(jbuf, 1); + longjmp(jbuf, JUMP_EXIT); } // if in graphics-thread return; @@ -262,7 +275,7 @@ void restart_sig(int signal_number) fprintf(stderr, "Caught SIGABRT in graphics thread\n"); app_debug_msg ("Caught SIGABRT in graphics thread. Jumping back to xwin_graphics_event_loop()...\n"); // jump back to entry-point in xwin_graphics_event_loop() - longjmp(jbuf, 1); + longjmp(jbuf, JUMP_ABORT); } // if ABRT raised in graphics-thread else { @@ -295,7 +308,7 @@ void restart_sig(int signal_number) // NOTE: this should only be called *ONCE* by an application void xwin_graphics_event_loop() { - int restarted; + int restarted = 0; app_debug_msg ("Direct call to xwin_graphics_event_loop()\n"); @@ -317,54 +330,46 @@ void xwin_graphics_event_loop() // THIS IS THE re-entry point at exit or ABORT in the graphics-thread restarted = setjmp(jbuf); - if (restarted) - app_debug_msg("xwin_graphics_event_loop() restarted at re-entry point\n"); - + // if we came here from an ABORT-signal thrown in this thread, we put this thread to sleep + if ( restarted == JUMP_ABORT ) + while(1) + sleep(1); + + //---------------------------------------------------------------------- + // running in standalone-mode: if ( boinc_is_standalone() ) { - if (restarted && !win) // now window was yet opened: just go to sleep to avoid more trouble... - { - app_debug_msg("Graphics exited before glut-window opened... continuing without graphics\n"); - while(1) sleep(1); - return; // should never arrive here - } // if restarted & no open windows - - if ( !restarted ) // first time through here + if (restarted) + while(1) sleep(1); // assuming glutInit() failed: put graphics-thread to sleep + else { // open the graphics-window set_mode(MODE_WINDOW); - glutTimerFunc(TIMER_INTERVAL_MSEC, timer_handler, 0); + glutTimerFunc(TIMER_INTERVAL_MSEC, timer_handler, 0); } - } // if standalone-mode - else // under BOINC: start with graphics hidden and wait for client to tell us otherwise + } // if boinc_is_standalone + //---------------------------------------------------------------------- + // runing under BOINC-client: + else { + if ( !glut_is_initialized ) + { + set_mode(MODE_HIDE_GRAPHICS); + while ( current_graphics_mode == MODE_HIDE_GRAPHICS ) + { + app_debug_msg ("Graphics-thread now waiting for client-message...\n"); + wait_for_initial_message(); + app_debug_msg ("got a graphics-message from client... \n"); + timer_handler(0); + } // while MODE_HIDE_GRAPHICS + } // glut_is_initialized = false + else + // here glut has been initialized previously + // probably the user pressed window-'close' + set_mode(MODE_HIDE_GRAPHICS); // close any previously open windows + }// if running under BOINC + //---------------------------------------------------------------------- - // Mac's GLUT is sufficiently "broken" that I simply don't see a way of restarting - // a GLUT-window after user exited once (e.g. using M-Q, or "Quit einstein" in menu). - // glut will segfault or similar at the attempt, so we rather don't try this in the first place. - // ==> If user exited glut-window once on mac, we put this thread to sleep - // (until somebody has a better idea / understanding of Mac's glut -#ifdef __APPLE_CC__ - if (restarted) - { - app_debug_msg("Sorry. I don't know a way of restarting glut on the Mac. Putting graphics to sleep now.\n"); - while(1) sleep(1); - return; - } -#endif - // on Linux on the other hand, glut seems to be happily restartable after exit was caught, - // so we simply continue as if it was the first time...: i.e. wait for client-message, then do something. - app_debug_msg ("Switching graphics-mode to MODE_HIDE_GRAPHICS\n"); - set_mode(MODE_HIDE_GRAPHICS); - while ( current_graphics_mode == MODE_HIDE_GRAPHICS ) - { - app_debug_msg ("Graphics-thread now waiting for client-message...\n"); - wait_for_initial_message(); - app_debug_msg ("got a graphics-message from client... \n"); - timer_handler(0); - } - } // ! boinc_is_standalone() - // ok we should be ready & initialized by now to call glutMainLoop() app_debug_msg ("now calling glutMainLoop()...\n"); glutMainLoop(); diff --git a/checkin_notes b/checkin_notes index 7a988cdfd6..fc01e8a888 100755 --- a/checkin_notes +++ b/checkin_notes @@ -22937,3 +22937,23 @@ David 18 Jan 2005 am_confirm.php am_create.php am_query.php + +Bruce 19 Jan 2005 + - Patches from Reinhard Prix to fix several problems. + (1) apps being suspended would call boinc_finish(nonzero) and + core client would treat this as app failure. Introduce + a new function boinc_exit() and call that instead. + (2) improvements to GUI build with wxWidgets + (3) improved X event handling loop now ensures that X window + is taken away when window killed. + configure.ac + clientgui/ + Makefile.am + api/ + boinc_api.h + boinc_api.C + x_opengl.C + lib/ + filesys.C + + diff --git a/clientgui/Makefile.am b/clientgui/Makefile.am index b478671114..03f6054886 100644 --- a/clientgui/Makefile.am +++ b/clientgui/Makefile.am @@ -46,7 +46,7 @@ EXTRA_DIST = BOINCBaseView.h DlgAttachProject.h ValidateAccountKey.h ViewWork boinc_gui_CPPFLAGS = $(AM_CPPFLAGS) $(WX_CPPFLAGS) $(CLIENTGUIFLAGS) boinc_gui_CXXFLAGS = $(AM_CXXFLAGS) $(WX_CXXFLAGS) $(CLIENTGUIFLAGS) -boinc_gui_LDADD = $(PTHREAD_LIBS) $(WX_LIBS) $(CLIENTGUILIBS) +boinc_gui_LDADD = $(PTHREAD_LIBS) $(WX_LIBS) all-local: client_gui-bin client_gui-bin: @CLIENT_GUI_BIN_FILENAME@ diff --git a/configure.ac b/configure.ac index bd84375ccd..88c4c20f17 100644 --- a/configure.ac +++ b/configure.ac @@ -307,24 +307,14 @@ WARNING: wxWidgets could not be found ==> building client without clientgui. fi AM_CONDITIONAL(BUILD_CLIENTGUI, [ test "$wxWin" = 1 -a "${enable_client}" = yes ]) + +if ( test "${enable_client}" = yes ) && ( test "$wxWin" = 1 ); then + CLIENTGUIFLAGS="-DNOCLIPBOARD -DNOTASKBAR" + AC_SUBST(CLIENTGUIFLAGS) +fi + dnl -------------------------------------------------------------------------------- - -dnl This is one way to set up host-specific stuff -case "${host}" in - *-*-linux* | *-*-solaris* ) - dnl no taskbar support in wxWidgets - CLIENTGUIFLAGS="-DNOCLIPBOARD -DNOTASKBAR"; - AC_SUBST(CLIENTGUIFLAGS) - AC_SUBST(CLIENTGUILIBS) ;; - *-*-darwin*) - dnl no taskbar support in wxWidgets - CLIENTGUIFLAGS="-DNOCLIPBOARD -DNOTASKBAR"; - CLIENTGUILIBS="-framework QuickTime -framework IOKit -framework Carbon -framework Cocoa -framework System -licon -framework WebKit"; - AC_SUBST(CLIENTGUIFLAGS) - AC_SUBST(CLIENTGUILIBS) ;; -esac - dnl Here's another way to set up host-specific stuff AM_CONDITIONAL(OS_DARWIN, [echo $host_os | grep '^darwin' > /dev/null]) AM_CONDITIONAL(OS_LINUX, [echo $host_os | grep '^linux' > /dev/null]) diff --git a/lib/filesys.C b/lib/filesys.C index 5a2d251d31..2a1175b64c 100755 --- a/lib/filesys.C +++ b/lib/filesys.C @@ -463,40 +463,50 @@ int boinc_make_dirs(const char* dirpath, const char* filepath) { return 0; } + +//---------------------------------------------------------------------- +// provide some file-locking mechanism to ensure only one app is running +// in a slot + +#if (!defined _WIN32) && (!defined HANDLE) +typedef int HANDLE; +#endif +HANDLE app_lockfile_handle; + int lock_file(char* filename) { int retval; - #ifdef _WIN32 - HANDLE hfile = CreateFile( + app_lockfile_handle = CreateFile( filename, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 - ); - if (hfile == INVALID_HANDLE_VALUE) retval = 1; - else - // NOTE: contrary to unix, we close the file on windows, so that we can remove it later in boinc_finish(). - // there seems to be no real file-locking done here, so this should be ok... - if (CloseHandle(hfile)) - retval = 0; - else - retval = 1; - -// some systems have both! + ); + if (app_lockfile_handle == INVALID_HANDLE_VALUE) + retval = 1; + // some systems have both! #elif defined(HAVE_LOCKF) && !defined(__APPLE__) - int fd = open(filename, O_WRONLY|O_CREAT, 0644); - retval = lockf(fd, F_TLOCK, 0); + app_lockfile_handle = open(filename, O_WRONLY|O_CREAT, 0644); + retval = lockf(app_lockfile_handle, F_TLOCK, 0); +#elif HAVE_FLOCK + app_lockfile_handle = open(filename, O_WRONLY|O_CREAT, 0644); + retval = flock(app_lockfile_handle, LOCK_EX|LOCK_NB); + // must leave file-handle open +#else + no file lock mechanism; +#endif + #ifndef NDEBUG if ( retval != 0 ) { struct flock lck; - fprintf(stdout, "DEBUG: Failed to get lockf on %s. Error = %d, %s\n", filename, errno, strerror(errno)); + perror ("DEBUG: Failed to get lock on application-lockfile"); lck.l_whence = 0; lck.l_start = 0L; lck.l_len = 0L; lck.l_type = F_WRLCK; - (void) fcntl(fd, F_GETLK, &lck); + (void) fcntl(app_lockfile_handle, F_GETLK, &lck); if (lck.l_type != F_UNLCK) fprintf(stdout, "DEBUG: Lock-info: pid = %d type = %c start = %8ld len = %8ld\n", lck.l_pid, - (lck.l_type == F_WRLCK) ? 'W' : 'R', lck.l_start, lck.l_len); + (lck.l_type == F_WRLCK) ? 'W' : 'R', lck.l_start, lck.l_len); else fprintf(stdout, "DEBUG: fcntl() says it's unlocked though....strange.\n"); @@ -504,14 +514,6 @@ int lock_file(char* filename) { } #endif // defined NDEBUG -#elif HAVE_FLOCK - int fd = open(filename, O_WRONLY|O_CREAT, 0644); - retval = flock(fd, LOCK_EX|LOCK_NB); - // must leave fd open -#else - no file lock mechanism; -#endif - return retval; }