screensaver: Implement new screensaver coordinator logic as requested by Rom.

svn path=/trunk/boinc/; revision=17322
This commit is contained in:
Charlie Fenton 2009-02-20 11:49:49 +00:00
parent 72e5c0fc0d
commit b4c78c27b4
8 changed files with 182 additions and 53 deletions

View File

@ -1290,7 +1290,7 @@ Charlie Feb 5 2009
clientscr/
screensaver.cpp
mac_saver_module.cpp
Mac_Saver_Module.cpp.h
Mac_Saver_Module.h
screensaver_win.h
ss_app.cpp
mac_build/
@ -1745,3 +1745,24 @@ Charlie 19 Feb 2009
clientscr/
ss_app.cpp
Charlie 20 Feb 2009
- screensaver: Implement new screensaver coordinator logic as requested by Rom:
If the coordinator cannot connect to the core client:
- coordinator retries connecting every 10 seconds
- coordinator launches the default graphics application with the
argument --retry_connect, so the default graphics app will continue
running and will also retry connecting every 10 seconds.
If the coordinator successfully connected to the core client, it launches the
default graphics application without the argument --retry_connect. If the
default graphics application can't connect, it will return immediately
with the exit code ERR_CONNECT. In that case, the coordinator assumes
the default graphics app was blocked by a firewall and so the coordinator
will run only project (science) graphics.
clientscr/
mac_saver_module.cpp
Mac_Saver_Module.h
ss_app.cpp
screensaver.cpp,.h
screensaver_win.cpp,.h

View File

@ -91,6 +91,7 @@ protected:
double m_fGFXChangePeriod;
bool m_bScience_gfx_running;
bool m_bDefault_gfx_running;
bool m_bConnected;
//
// Data management layer
@ -107,6 +108,7 @@ protected:
void HandleRPCError(void);
OSErr KillScreenSaver(void);
void GetDisplayPeriods(char *dir_path);
bool HasProcessExited(pid_t pid, int &exitCode);
pthread_t m_hDataManagementThread;
pid_t m_hGraphicsApplication;
@ -116,7 +118,7 @@ protected:
// Count the number of active graphics-capable apps
int count_active_graphic_apps(RESULTS& results, RESULT* exclude = NULL);
// Choose a ramdom graphics application from the vector that
// Choose a random graphics application from the vector that
// was passed in.
RESULT* get_random_graphics_app(RESULTS& results, RESULT* exclude = NULL);

View File

@ -83,6 +83,7 @@ static long gSystemVersion = 0;
const char * CantLaunchCCMsg = "Unable to launch BOINC application.";
const char * LaunchingCCMsg = "Launching BOINC application.";
const char * ConnectingCCMsg = "Connecting to BOINC application.";
const char * ConnectedCCMsg = "Communicating with BOINC application.";
const char * BOINCSuspendedMsg = "BOINC is currently suspended.";
const char * BOINCNoAppsExecutingMsg = "BOINC is currently idle.";
const char * BOINCNoProjectsDetectedMsg = "BOINC is not attached to any projects. Please attach to projects using the BOINC Manager.";
@ -174,6 +175,7 @@ CScreensaver::CScreensaver() {
m_hGraphicsApplication = NULL;
m_bResetCoreState = TRUE;
rpc = 0;
m_bConnected = false;
err = Gestalt(gestaltSystemVersion, &gSystemVersion);
if (err != noErr) {
@ -262,7 +264,7 @@ OSStatus CScreensaver::initBOINCApp() {
m_CoreClientPID = FindProcessPID("boinc", 0);
if (m_CoreClientPID) {
m_wasAlreadyRunning = true;
m_wasAlreadyRunning = true;
saverState = SaverState_LaunchingCoreClient;
return noErr;
}
@ -344,11 +346,14 @@ int CScreensaver::getSSMessage(char **theMessage, int* coveredFreq) {
setSSMessageText(LaunchingCCMsg);
myPid = FindProcessPID(NULL, m_CoreClientPID);
if (myPid) {
if (myPid) {
saverState = SaverState_CoreClientRunning;
rpc->init(NULL); // Initialize communications with Core Client
if (!rpc->init(NULL)) { // Initialize communications with Core Client
m_bConnected = true;
}
// Set up a separate thread for communicating with Core Client
// Set up a separate thread for communicating with Core Client
// and running screensaver graphics
CreateDataManagementThread();
// ToDo: Add a timeout after which we display error message
} else
@ -371,6 +376,7 @@ int CScreensaver::getSSMessage(char **theMessage, int* coveredFreq) {
case SaverState_ConnectedToCoreClient:
switch (m_hrError) {
case 0:
setSSMessageText(ConnectedCCMsg);
break; // No status response yet from DataManagementProc
case SCRAPPERR_SCREENSAVERBLANKED:
default:
@ -447,6 +453,9 @@ int CScreensaver::getSSMessage(char **theMessage, int* coveredFreq) {
case SaverState_CantLaunchCoreClient:
setSSMessageText(CantLaunchCCMsg);
// Set up a separate thread for running screensaver graphics
// even if we can't communicate with core client
CreateDataManagementThread();
break;
case SaverState_Idle:
@ -507,19 +516,32 @@ void * CScreensaver::DataManagementProcStub(void* param) {
void CScreensaver::HandleRPCError() {
static time_t last_RPC_retry = 0;
// Attempt to restart BOINC Client if needed, reinitialize the RPC client and state
rpc->close();
m_bConnected = false;
// There is a possible race condition where the Core Client was in the
// process of shutting down just as ScreenSaver started, so initBOINCApp()
// found it already running but now it has shut down. This code takes
// care of that and other situations where the Core Client quits unexpectedy.
if (FindProcessPID("boinc", 0) == 0) {
saverState = SaverState_RelaunchCoreClient;
m_bResetCoreState = true;
}
rpc->init(NULL); // Otherwise just reinitialize the RPC client and state and keep trying
if (saverState == SaverState_CantLaunchCoreClient) {
if ((time(0) - last_RPC_retry) < RPC_RETRY_INTERVAL) {
return;
}
} else {
// There is a possible race condition where the Core Client was in the
// process of shutting down just as ScreenSaver started, so initBOINCApp()
// found it already running but now it has shut down. This code takes
// care of that and other situations where the Core Client quits unexpectedy.
// Code in initBOINC_App() limits # launch retries to 3 to prevent thrashing.
if (FindProcessPID("boinc", 0) == 0) {
saverState = SaverState_RelaunchCoreClient;
m_bResetCoreState = true;
}
}
// Otherwise just reinitialize the RPC client and state and keep trying
if (!rpc->init(NULL)) {
m_bConnected = true;
}
// Error message after timeout?
}

View File

@ -35,6 +35,7 @@
#include "util.h"
#include "common_defs.h"
#include "filesys.h"
#include "error_numbers.h"
#ifdef _WIN32
@ -45,11 +46,13 @@
#define PATH_SEPARATOR (_T("\\"))
#define THE_DEFAULT_SS_EXECUTABLE (_T(DEFAULT_SS_EXECUTABLE))
#define THE_SS_CONFIG_FILE (_T(SS_CONFIG_FILE))
#define DEFAULT_GFX_CANT_CONNECT ERR_CONNECT
#else
// Using (_T()) here causes compiler errors on Mac
#define PATH_SEPARATOR "/"
#define THE_DEFAULT_SS_EXECUTABLE DEFAULT_SS_EXECUTABLE
#define THE_SS_CONFIG_FILE SS_CONFIG_FILE
#define DEFAULT_GFX_CANT_CONNECT (ERR_CONNECT & 0xff)
#endif
#ifdef __APPLE__
@ -324,6 +327,7 @@ int CScreensaver::launch_default_screensaver(char *dir_path, int& graphics_appli
{
int retval = 0;
char full_path[1024];
int num_args = 2;
strlcpy(full_path, dir_path, sizeof(full_path));
strlcat(full_path, PATH_SEPARATOR, sizeof(full_path));
@ -333,14 +337,20 @@ int CScreensaver::launch_default_screensaver(char *dir_path, int& graphics_appli
// "RegisterProcess failed (error = -50)" unless we pass its
// full path twice in the argument list to execv on Macs.
char* argv[3];
char* argv[4];
argv[0] = full_path; // not used
argv[1] = "--fullscreen";
argv[2] = 0;
argv[3] = 0;
if (!m_bConnected) {
BOINCTRACE(_T("launch_default_screensaver using --retry_connect argument\n"));
argv[2] = "--retry_connect";
num_args = 3;
}
retval = run_program(
dir_path,
full_path,
2,
num_args,
argv,
0,
graphics_application
@ -351,6 +361,16 @@ int CScreensaver::launch_default_screensaver(char *dir_path, int& graphics_appli
}
// If we cannot connect to the core client:
// - we retry connecting every 10 seconds
// - we launch the default graphics application with the argument --retry_connect, so
// it will continue running and will also retry connecting every 10 seconds.
//
// If we successfully connected to the core client, launch the default graphics application
// without the argument --retry_connect. If it can't connect, it will return immediately
// with the exit code ERR_CONNECT. In that case, we assume it was blocked by a firewall
// and so we run only project (science) graphics.
#ifdef _WIN32
DWORD WINAPI CScreensaver::DataManagementProc()
#else
@ -376,6 +396,7 @@ void *CScreensaver::DataManagementProc()
SS_PHASE ss_phase = DEFAULT_SS_PHASE;
bool switch_to_default_gfx = false;
int exit_status = 0;
char* default_ss_dir_path = NULL;
char* default_data_dir_path = NULL;
@ -480,32 +501,39 @@ retval = 0;
m_QuitDataManagementProc = true;
}
// Do we need to get the core client state?
if (m_bResetCoreState) {
// Try and get the current state of the CC
retval = rpc->get_state(state);
if (retval) {
// CC may not yet be running
HandleRPCError();
continue;
}
m_bResetCoreState = false;
}
BOINCTRACE(_T("CScreensaver::DataManagementProc - ErrorMode = '%d', ErrorCode = '%x'\n"), m_bErrorMode, m_hrError);
// Update our task list
m_updating_results = true;
retval = rpc->get_screensaver_tasks(suspend_reason, results);
m_updating_results = false;
if (retval) {
// rpc call returned error
if (! m_bConnected) {
HandleRPCError();
m_bResetCoreState = true;
continue;
}
if (m_bConnected) {
// Do we need to get the core client state?
if (m_bResetCoreState) {
// Try and get the current state of the CC
retval = rpc->get_state(state);
if (retval) {
// CC may not yet be running
HandleRPCError();
} else {
m_bResetCoreState = false;
}
}
// Update our task list
m_updating_results = true;
retval = rpc->get_screensaver_tasks(suspend_reason, results);
m_updating_results = false;
if (retval) {
// rpc call returned error
HandleRPCError();
m_bResetCoreState = true;
continue;
}
} else {
results.clear();
}
// Time to switch to default graphics phase?
if (m_bDefault_ss_exists && (ss_phase == SCIENCE_SS_PHASE)) {
if (science_phase_start_time && ((dtime() - science_phase_start_time) > m_fGFxSciencePeriod)) {
@ -524,7 +552,7 @@ retval = 0;
}
// Time to switch to science graphics phase?
if (ss_phase == DEFAULT_SS_PHASE) {
if ((ss_phase == DEFAULT_SS_PHASE) && m_bConnected) {
if (default_phase_start_time &&
((dtime() - default_phase_start_time + default_saver_duration_in_science_phase)
> m_fGFXDefaultPeriod)) {
@ -739,10 +767,18 @@ retval = 0;
// Is the graphics app still running?
if (m_hGraphicsApplication) {
if (!process_exists(m_hGraphicsApplication)) {
if (HasProcessExited(m_hGraphicsApplication, exit_status)) {
// Something has happened to the previously selected screensaver
// application. Start a different one.
BOINCTRACE(_T("CScreensaver::DataManagementProc - Graphics application isn't running, start a new one.\n"));
if (m_bDefault_gfx_running) {
// If we were able to connect to core client but gfx app can't, don't use it.
BOINCTRACE(_T("CScreensaver::DataManagementProc - Default graphics application exited with code %d.\n"), exit_status);
if (exit_status == DEFAULT_GFX_CANT_CONNECT) {
m_bDefault_ss_exists = false;
ss_phase = SCIENCE_SS_PHASE;
}
}
m_hGraphicsApplication = 0;
graphics_app_result_ptr = NULL;
m_bDefault_gfx_running = false;
@ -759,6 +795,33 @@ retval = 0;
}
#ifdef _WIN32
BOOL CScreensaver::HasProcessExited(HANDLE pid_handle, int &exitCode) {
unsigned long status = 1;
if (GetExitCodeProcess(pid_handle, &status)) {
if (status == STILL_ACTIVE) {
exitCode = 0;
return false;
}
}
exitCode = (int)status;
return true;
}
#else
bool CScreensaver::HasProcessExited(pid_t pid, int &exitCode) {
int status;
pid_t p;
p = waitpid(pid, &status, WNOHANG);
exitCode = WEXITSTATUS(status);
if (p == pid) return true; // process has exited
if (p == -1) return true; // PID doesn't exist
exitCode = 0;
return false;
}
#endif
void CScreensaver::GetDisplayPeriods(char *dir_path) {
char buf[1024];
FILE* f;

View File

@ -29,6 +29,8 @@ enum SS_PHASE {
SCIENCE_SS_PHASE
};
#define RPC_RETRY_INTERVAL 10 /* # of seconds between retries connecting to core client */
//-----------------------------------------------------------------------------
// Error / status codes
//-----------------------------------------------------------------------------

View File

@ -184,6 +184,7 @@ CScreensaver::CScreensaver() {
m_dwBlankTime = 0;
rpc = NULL;
m_bConnected = false;
m_hDataManagementThread = NULL;
m_hGraphicsApplication = NULL;
m_bResetCoreState = TRUE;
@ -954,6 +955,8 @@ BOOL CScreensaver::CreateDataManagementThread() {
BOINCTRACE(_T("CScreensaver::CreateDataManagementThread: Failed to create data management thread '%d'\n"), GetLastError());
return FALSE;
}
m_tThreadCreateTime = time(0);
return TRUE;
}
@ -995,12 +998,26 @@ DWORD WINAPI CScreensaver::DataManagementProcStub(LPVOID UNUSED(lpParam)) {
void CScreensaver::HandleRPCError()
{
// Attempt to reinitialize the RPC client and state
rpc->close();
rpc->init(NULL);
m_bResetCoreState = TRUE;
static time_t last_RPC_retry = 0;
time_t now = time(0);
if ((time(0) - m_tThreadCreateTime) > 3) {
rpc->close();
m_bConnected = false;
if ((now - m_tThreadCreateTime) > 3) {
if ((now - last_RPC_retry) < RPC_RETRY_INTERVAL) {
return;
}
}
// Attempt to reinitialize the RPC client and state
if (!rpc->init(NULL)) {
m_bConnected = true;
m_bResetCoreState = TRUE;
return;
}
if ((now - m_tThreadCreateTime) > 3) {
SetError(TRUE, SCRAPPERR_BOINCNOTDETECTED);
}
}

View File

@ -144,7 +144,6 @@ protected:
DWORD m_dwBlankScreen;
DWORD m_dwBlankTime;
//
// Data management layer
//
@ -164,6 +163,7 @@ protected:
int launch_default_screensaver(char *dir_path, HANDLE& graphics_application);
void HandleRPCError(void);
void GetDisplayPeriods(char *dir_path);
BOOL HasProcessExited(HANDLE pid_handle, int &exitCode);
// Determine if two RESULT pointers refer to the same task
bool is_same_task(RESULT* taska, RESULT* taskb);
@ -197,6 +197,7 @@ protected:
bool m_bScience_gfx_running;
bool m_bDefault_gfx_running;
BOOL m_bConnected;
//
// Presentation layer

View File

@ -44,6 +44,7 @@
#include "gui_rpc_client.h"
#include "util.h"
#include "app_ipc.h"
#include "error_numbers.h"
using std::string;
using std::vector;
@ -365,7 +366,7 @@ void app_graphics_render(int xs, int ys, double t) {
}
if (retval) {
if (!retry_connect) {
exit(retval);
exit(ERR_CONNECT);
}
next_connect_time = t + 10;
} else {
@ -390,7 +391,7 @@ void app_graphics_render(int xs, int ys, double t) {
retval = update_data();
if (retval) {
if (!retry_connect) {
exit(retval);
exit(ERR_CONNECT);
}
connected = false;
next_connect_time = t + 10;
@ -446,10 +447,10 @@ int main(int argc, char** argv) {
bool test = false;
for (int i=1; i<argc; i++) {
if (!strcmp(argv[1], "--test")) {
if (!strcmp(argv[i], "--test")) {
test = true;
}
if (!strcmp(argv[1], "--retry_connect")) {
if (!strcmp(argv[i], "--retry_connect")) {
retry_connect = true;
}
}
@ -462,7 +463,7 @@ int main(int argc, char** argv) {
if (!retval) {
retval = update_data();
}
exit(retval);
exit(ERR_CONNECT);
}
boinc_graphics_loop(argc, argv);