diff --git a/api/boinc_api.C b/api/boinc_api.C
index 4d889655bf..0a8db1b23b 100644
--- a/api/boinc_api.C
+++ b/api/boinc_api.C
@@ -66,6 +66,7 @@ extern BOOL win_loop_done;
#endif
#include "parse.h"
+#include "shmem.h"
#include "util.h"
#include "error_numbers.h"
#include "graphics_api.h"
@@ -97,6 +98,7 @@ static bool write_frac_done = false;
static bool this_process_active;
static bool time_to_quit = false;
bool using_opengl = false;
+static APP_CLIENT_SHM *app_client_shm;
// read the INIT_DATA and FD_INIT files
//
@@ -165,6 +167,7 @@ int boinc_init() {
boinc_install_signal_handlers();
set_timer(timer_period);
+ setup_shared_mem();
return 0;
}
@@ -263,7 +266,7 @@ void boinc_quit(int sig) {
int boinc_finish(int status) {
last_checkpoint_cpu_time = boinc_cpu_time();
- write_fraction_done_file(fraction_done,last_checkpoint_cpu_time,last_checkpoint_cpu_time);
+ update_app_progress(fraction_done, last_checkpoint_cpu_time, last_checkpoint_cpu_time);
#ifdef _WIN32
// Stop the timer
timeKillEvent(timer_id);
@@ -277,6 +280,7 @@ int boinc_finish(int status) {
}
#endif
#endif
+ cleanup_shared_mem();
exit(status);
return 0;
}
@@ -317,13 +321,13 @@ bool boinc_time_to_checkpoint() {
switch (eventState) {
case WAIT_OBJECT_0:
case WAIT_ABANDONED:
- time_to_quit = true;
- break;
+ time_to_quit = true;
+ break;
}
#endif
if (write_frac_done) {
- write_fraction_done_file(fraction_done, boinc_cpu_time(), last_checkpoint_cpu_time);
+ update_app_progress(fraction_done, boinc_cpu_time(), last_checkpoint_cpu_time);
time_until_fraction_done_update = aid.fraction_done_update_period;
write_frac_done = false;
}
@@ -339,7 +343,7 @@ bool boinc_time_to_checkpoint() {
int boinc_checkpoint_completed() {
last_checkpoint_cpu_time = boinc_cpu_time();
- write_fraction_done_file(fraction_done,last_checkpoint_cpu_time,last_checkpoint_cpu_time);
+ update_app_progress(fraction_done, last_checkpoint_cpu_time, last_checkpoint_cpu_time);
ready_to_checkpoint = false;
time_until_checkpoint = aid.checkpoint_period;
// If it's time to quit, call boinc_finish which will
@@ -403,7 +407,7 @@ double boinc_cpu_time() {
totTime = tKernel.QuadPart + tUser.QuadPart;
// Runtimes in 100-nanosecond units
- cpu_secs += totTime / 10000000.0;
+ cpu_secs += totTime / 1.e7;
// Convert to seconds and return
return cpu_secs;
@@ -503,6 +507,45 @@ int set_timer(double period) {
return retval;
}
+void setup_shared_mem(void) {
+#ifdef API_IGNORE_CLIENT
+ fprintf( stderr, "Ignoring client, so not attaching to shared memory.\n" );
+ return;
+#endif
+
+#ifdef HAVE_SYS_SHM_H
+#ifdef HAVE_SYS_IPC_H
+ if (attach_shmem(aid.shm_key, (void**)&app_client_shm)) {
+ app_client_shm = NULL;
+ }
+#endif
+#endif
+}
+
+void cleanup_shared_mem(void) {
+#ifdef HAVE_SYS_SHM_H
+#ifdef HAVE_SYS_IPC_H
+ if (app_client_shm != NULL)
+ detach_shmem(app_client_shm);
+#endif
+#endif
+}
+
+int update_app_progress(double frac_done, double cpu_t, double cp_cpu_t) {
+ if (app_client_shm == NULL || (app_client_shm->access != APP_ACCESS_OK))
+ return -1;
+
+ sprintf( app_client_shm->message_buf,
+ "%f\n"
+ "%f\n"
+ "%f\n",
+ frac_done, cpu_t, cp_cpu_t );
+
+ app_client_shm->access = CLIENT_ACCESS_OK;
+
+ return 0;
+}
+
int write_init_data_file(FILE* f, APP_INIT_DATA& ai) {
if (strlen(ai.app_preferences)) {
fprintf(f, "\n%s\n", ai.app_preferences);
@@ -522,6 +565,7 @@ int write_init_data_file(FILE* f, APP_INIT_DATA& ai) {
"%f\n"
"%f\n"
"%f\n"
+ "%d\n"
"%f\n"
"%f\n",
ai.wu_cpu_time,
@@ -529,6 +573,7 @@ int write_init_data_file(FILE* f, APP_INIT_DATA& ai) {
ai.user_expavg_credit,
ai.host_total_credit,
ai.host_expavg_credit,
+ ai.shm_key,
ai.checkpoint_period,
ai.fraction_done_update_period
);
@@ -555,6 +600,7 @@ int parse_init_data_file(FILE* f, APP_INIT_DATA& ai) {
else if (parse_double(buf, "", ai.host_total_credit)) continue;
else if (parse_double(buf, "", ai.host_expavg_credit)) continue;
else if (parse_double(buf, "", ai.wu_cpu_time)) continue;
+ else if (parse_int(buf, "", ai.shm_key)) continue;
else if (parse_double(buf, "", ai.checkpoint_period)) continue;
else if (parse_double(buf, "", ai.fraction_done_update_period)) continue;
else fprintf(stderr, "parse_init_data_file: unrecognized %s", buf);
@@ -562,40 +608,6 @@ int parse_init_data_file(FILE* f, APP_INIT_DATA& ai) {
return 0;
}
-int write_fraction_done_file(double pct, double cpu, double checkpoint_cpu) {
- FILE* f = fopen(FRACTION_DONE_TEMP_FILE, "w");
-
- if (!f) return -1;
-
- fprintf(f,
- "%f\n"
- "%f\n"
- "%f\n",
- pct,
- cpu,
- checkpoint_cpu
- );
-
- fclose(f);
-#ifdef _WIN32
- unlink(FRACTION_DONE_FILE);
-#endif
- rename(FRACTION_DONE_TEMP_FILE, FRACTION_DONE_FILE);
-
- return 0;
-}
-
-int parse_fraction_done_file(FILE* f, double& pct, double& cpu, double& checkpoint_cpu) {
- char buf[256];
- while (fgets(buf, 256, f)) {
- if (parse_double(buf, "", pct)) continue;
- else if (parse_double(buf, "", cpu)) continue;
- else if (parse_double(buf, "", checkpoint_cpu)) continue;
- else fprintf(stderr, "parse_fraction_done_file: unrecognized %s", buf);
- }
- return 0;
-}
-
// TODO: this should handle arbitrarily many fd/filename pairs.
// Also, give the tags better names
int write_fd_init_file(FILE* f, char *file_name, int fdesc, int input_file ) {
diff --git a/api/boinc_api.h b/api/boinc_api.h
index 9168cf2b59..4e894b08f0 100755
--- a/api/boinc_api.h
+++ b/api/boinc_api.h
@@ -28,7 +28,7 @@
#ifndef _BOINC_API
#define _BOINC_API
-#define DEFAULT_FRACTION_DONE_UPDATE_PERIOD 10
+#define DEFAULT_FRACTION_DONE_UPDATE_PERIOD 1
#define DEFAULT_CHECKPOINT_PERIOD 300
// MFILE supports a primitive form of checkpointing.
@@ -62,9 +62,18 @@ struct APP_INIT_DATA {
double host_total_credit;
double host_expavg_credit;
double checkpoint_period; // recommended checkpoint period
+ int shm_key;
double fraction_done_update_period;
};
+#define APP_ACCESS_OK 0
+#define CLIENT_ACCESS_OK 1
+
+struct APP_CLIENT_SHM {
+ int access;
+ char message_buf[4096];
+};
+
extern int boinc_init();
extern int boinc_get_init_data(APP_INIT_DATA&);
extern int boinc_finish(int);
@@ -79,20 +88,19 @@ extern int boinc_install_signal_handlers();
/////////// API ENDS HERE - IMPLEMENTATION STUFF FOLLOWS
+int update_app_progress(double, double, double);
int write_init_data_file(FILE* f, APP_INIT_DATA&);
int parse_init_data_file(FILE* f, APP_INIT_DATA&);
int write_fd_init_file(FILE*, char*, int, int);
int parse_fd_init_file(FILE*);
-int write_fraction_done_file(double, double, double);
-int parse_fraction_done_file(FILE*, double&, double&, double&);
#define INIT_DATA_FILE "init_data.xml"
#define GRAPHICS_DATA_FILE "graphics.xml"
#define FD_INIT_FILE "fd_init.xml"
-#define FRACTION_DONE_FILE "fraction_done.xml"
-#define FRACTION_DONE_TEMP_FILE "fraction_done.tmp"
#define STDERR_FILE "stderr.txt"
int set_timer(double period);
+void setup_shared_mem();
+void cleanup_shared_mem();
#endif
diff --git a/apps/Makefile.in b/apps/Makefile.in
index c167586577..388a096733 100644
--- a/apps/Makefile.in
+++ b/apps/Makefile.in
@@ -16,7 +16,7 @@ CC = @CC@ $(CFLAGS) -I @top_srcdir@/api -I@top_srcdir@/lib
APIOBJS = ../api/boinc_api.o ../api/graphics_api.o
X11APIOBJS = ../api/boinc_api.x11.o ../api/graphics_api.x11.o ../api/x_opengl.x11.o
-LIBS = ../api/mfile.o ../lib/parse.o ../lib/filesys.o ../lib/util.o
+LIBS = ../api/mfile.o ../lib/parse.o ../lib/filesys.o ../lib/shmem.o ../lib/util.o
CLIBS = @LIBS@
diff --git a/checkin_notes b/checkin_notes
index c284e89079..5852a9b36d 100755
--- a/checkin_notes
+++ b/checkin_notes
@@ -3819,3 +3819,20 @@ David Mar 15 2003
sched/
feeder.C
+Eric March 17, 2003
+ - Changed app->client communication to use shared memory rather
+ than files. The client sets up a shared memory segment when
+ starting the app. The app attaches to it and writes XML tags
+ into a 4K text buffer every second.
+
+ configure.in
+ api/
+ boinc_api.C,h
+ apps/
+ Makefile.in
+ client/
+ app.C,h
+ cs_apps.C
+ lib/
+ shmem.C,h
+
diff --git a/client/app.C b/client/app.C
index 9c37207b2b..30c107c086 100644
--- a/client/app.C
+++ b/client/app.C
@@ -64,6 +64,7 @@
#include "file_names.h"
#include "log_flags.h"
#include "parse.h"
+#include "shmem.h"
#include "util.h"
#include "app.h"
@@ -86,6 +87,7 @@ ACTIVE_TASK::ACTIVE_TASK() {
result = NULL;
wup = NULL;
app_version = NULL;
+ app_client_shm = NULL;
pid = 0;
slot = 0;
state = PROCESS_UNINITIALIZED;
@@ -151,6 +153,7 @@ int ACTIVE_TASK::start(bool first_time) {
aid.host_expavg_credit = wup->project->host_expavg_credit;
aid.checkpoint_period = DEFAULT_CHECKPOINT_PERIOD;
aid.fraction_done_update_period = DEFAULT_FRACTION_DONE_UPDATE_PERIOD;
+ aid.shm_key = 0;
aid.wu_cpu_time = checkpoint_cpu_time;
sprintf(init_data_path, "%s%s%s", slot_dir, PATH_SEPARATOR, INIT_DATA_FILE);
@@ -163,6 +166,9 @@ int ACTIVE_TASK::start(bool first_time) {
show_message(wup->project, buf, MSG_ERROR);
return ERR_FOPEN;
}
+#ifdef HAVE_SYS_IPC_H
+ aid.shm_key = ftok( init_data_path, slot );
+#endif
retval = write_init_data_file(f, aid);
if (retval) return retval;
@@ -321,9 +327,17 @@ int ACTIVE_TASK::start(bool first_time) {
thread_handle = process_info.hThread;
#else
char* argv[100];
+
+ // Setup shared memory to communicate between processes
+ // The app must attach to this shmem segment by calling boinc_init()
+ //
+ shm_key = aid.shm_key;
+
+ // Destroy any shared memory still hanging around from previous runs
+ //destroy_shmem(shm_key);
+
pid = fork();
if (pid == 0) {
-
// from here on we're running in a new process.
// If an error happens, exit nonzero so that the core client
// knows there was a problem.
@@ -349,6 +363,13 @@ int ACTIVE_TASK::start(bool first_time) {
perror("execv");
exit(1);
}
+
+ // Create shared memory after forking, to prevent problems with the
+ // child inheriting bad information about the segment
+ if (!create_shmem(shm_key, sizeof(APP_CLIENT_SHM), (void**)&app_client_shm)) {
+ app_client_shm->access = APP_ACCESS_OK;
+ }
+
if (log_flags.task_debug) printf("forked process: pid %d\n", pid);
#endif
state = PROCESS_RUNNING;
@@ -415,28 +436,13 @@ bool ACTIVE_TASK_SET::poll() {
#ifdef _WIN32
unsigned long exit_code;
- FILETIME creation_time, exit_time, kernel_time, user_time;
- ULARGE_INTEGER tKernel, tUser;
- LONGLONG totTime;
bool found = false;
for (int i=0; ipid_handle, &exit_code)) {
- //
- // Get the elapsed CPU time
- if (GetProcessTimes(
- atp->pid_handle, &creation_time, &exit_time,
- &kernel_time, &user_time
- )) {
- tKernel.LowPart = kernel_time.dwLowDateTime;
- tKernel.HighPart = kernel_time.dwHighDateTime;
- tUser.LowPart = user_time.dwLowDateTime;
- tUser.HighPart = user_time.dwHighDateTime;
-
- // Runtimes in 100-nanosecond units
- totTime = tKernel.QuadPart + tUser.QuadPart;
- }
+ // Get the elapsed CPU time, checkpoint CPU time
+ atp->check_app_status();
atp->result->final_cpu_time = atp->checkpoint_cpu_time;
if (exit_code != STILL_ACTIVE) {
found = true;
@@ -469,11 +475,10 @@ bool ACTIVE_TASK_SET::poll() {
}
if (found) return true;
#else
- struct rusage rs;
int pid;
int stat;
- pid = wait3(&stat, WNOHANG, &rs);
+ pid = wait3(&stat, WNOHANG, 0);
if (pid > 0) {
if (log_flags.task_debug) printf("process %d is done\n", pid);
atp = lookup_pid(pid);
@@ -481,11 +486,11 @@ bool ACTIVE_TASK_SET::poll() {
fprintf(stderr, "ACTIVE_TASK_SET::poll(): pid %d not found\n", pid);
return true;
}
- double x = rs.ru_utime.tv_sec + rs.ru_utime.tv_usec/1.e6;
- atp->result->final_cpu_time = atp->starting_cpu_time + x;
+ atp->check_app_status();
+ atp->result->final_cpu_time = atp->checkpoint_cpu_time;
if (atp->state == PROCESS_ABORT_PENDING) {
atp->state = PROCESS_ABORTED;
- atp->result->active_task_state = PROCESS_ABORTED;
+ atp->result->active_task_state = PROCESS_ABORTED;
gstate.report_result_error(
*(atp->result), 0, "process was aborted\n"
);
@@ -525,6 +530,7 @@ bool ACTIVE_TASK_SET::poll() {
}
}
+ destroy_shmem(atp->shm_key);
atp->read_stderr_file();
clean_out_dir(atp->slot_dir);
@@ -573,8 +579,8 @@ bool ACTIVE_TASK::read_stderr_file() {
//
int ACTIVE_TASK_SET::wait_for_exit(double wait_time) {
bool all_exited;
- unsigned int i,n;
- ACTIVE_TASK *atp;
+ unsigned int i,n;
+ ACTIVE_TASK *atp;
for (i=0; i<10; i++) {
boinc_sleep(wait_time/10.0);
@@ -588,9 +594,7 @@ int ACTIVE_TASK_SET::wait_for_exit(double wait_time) {
}
}
- if (all_exited) {
- return 0;
- }
+ if (all_exited) return 0;
}
return -1;
@@ -745,31 +749,71 @@ int ACTIVE_TASK_SET::restart_tasks() {
return 0;
}
-// See if the app has generated a new fraction-done file.
+int ACTIVE_TASK::get_cpu_time() {
+#ifdef _WIN32
+ FILETIME creation_time, exit_time, kernel_time, user_time;
+ ULARGE_INTEGER tKernel, tUser;
+ LONGLONG totTime;
+
+ // Get the elapsed CPU time
+ if (GetProcessTimes( pid_handle, &creation_time, &exit_time, &kernel_time, &user_time )) {
+ tKernel.LowPart = kernel_time.dwLowDateTime;
+ tKernel.HighPart = kernel_time.dwHighDateTime;
+ tUser.LowPart = user_time.dwLowDateTime;
+ tUser.HighPart = user_time.dwHighDateTime;
+
+ // Runtimes in 100 nanosecond units
+ totTime = tKernel.QuadPart + tUser.QuadPart;
+ current_cpu_time = checkpoint_cpu_time = starting_cpu_time + totTime/1.e7;
+ return 0;
+ }
+#else
+ struct rusage rs;
+ pid_t ret_pid;
+ int stat;
+ ret_pid = wait4(pid, &stat, WNOHANG, &rs);
+ if (ret_pid > 0) {
+ double x = rs.ru_utime.tv_sec + rs.ru_utime.tv_usec/1.e6;
+ current_cpu_time = checkpoint_cpu_time = starting_cpu_time + x;
+ return 0;
+ }
+#endif
+ return -1;
+}
+
+// See if the app has generated a new fraction-done buffer.
// If so read it and return true.
//
-bool ACTIVE_TASK::check_app_status_files() {
- FILE* f;
- char path[256];
- bool found = false;
- int retval;
-
- sprintf(path, "%s%s%s", slot_dir, PATH_SEPARATOR, FRACTION_DONE_FILE);
- f = fopen(path, "r");
- if (f) {
- found = true;
- retval = parse_fraction_done_file(f, fraction_done, current_cpu_time, checkpoint_cpu_time);
- fclose(f);
- if (retval) return false;
- retval = file_delete(path);
- if (retval) {
- fprintf(stderr,
- "ACTIVE_TASK.check_app_status_files: could not delete %s: %d\n",
- path, retval
- );
+bool ACTIVE_TASK::check_app_status() {
+ if (app_client_shm == NULL) {
+ fraction_done = 0;
+ get_cpu_time();
+ } else {
+ if (app_client_shm->access == CLIENT_ACCESS_OK) {
+ fraction_done = current_cpu_time = checkpoint_cpu_time = 0.0;
+
+ parse_double(app_client_shm->message_buf, "", fraction_done);
+ parse_double(app_client_shm->message_buf, "", current_cpu_time);
+ parse_double(app_client_shm->message_buf, "", checkpoint_cpu_time);
+
+ app_client_shm->access = APP_ACCESS_OK;
+
+ return false;
}
}
- return found;
+
+ return false;
+}
+
+// Check status of all active tasks
+//
+void ACTIVE_TASK_SET::check_apps() {
+ unsigned int i;
+ ACTIVE_TASK *atp;
+ for (i=0; icheck_app_status();
+ }
}
// Returns the estimated time to completion (in seconds) of this task,
@@ -811,7 +855,7 @@ bool ACTIVE_TASK_SET::poll_time() {
for (i=0; icheck_app_status_files();
+ updated |= atp->check_app_status();
}
return updated;
diff --git a/client/app.h b/client/app.h
index a4b7f6aa27..c8d3f6e2e2 100644
--- a/client/app.h
+++ b/client/app.h
@@ -28,6 +28,7 @@
#include
#include
#include "client_types.h"
+#include "boinc_api.h"
class CLIENT_STATE;
typedef int PROCESS_ID;
@@ -55,11 +56,14 @@ class ACTIVE_TASK {
public:
#ifdef _WIN32
HANDLE pid_handle, thread_handle, quitRequestEvent;
+#else
+ key_t shm_key;
#endif
RESULT* result;
WORKUNIT* wup;
APP_VERSION* app_version;
PROCESS_ID pid;
+ APP_CLIENT_SHM *app_client_shm;
int slot; // which slot (determines directory)
int state;
int exit_status;
@@ -98,7 +102,8 @@ public:
int suspend();
int unsuspend();
- bool check_app_status_files();
+ int get_cpu_time();
+ bool check_app_status();
double est_time_to_completion();
bool read_stderr_file();
@@ -120,6 +125,7 @@ public:
int restart_tasks();
void request_tasks_exit();
void kill_tasks();
+ void check_apps();
int get_free_slot(int total_slots);
int write(FILE*);
diff --git a/client/cs_apps.C b/client/cs_apps.C
index ed522ffef5..cf505c5a52 100644
--- a/client/cs_apps.C
+++ b/client/cs_apps.C
@@ -59,15 +59,17 @@ int CLIENT_STATE::cleanup_and_exit() {
}
int CLIENT_STATE::exit_tasks() {
+ // TODO: unsuspend active tasks so they have a chance to checkpoint
// Send a request to the tasks to exit
active_tasks.request_tasks_exit();
- // Wait a second for them to exit normally
- active_tasks.wait_for_exit(1);
-
- // And then just kill them
- active_tasks.kill_tasks();
+ // Wait a second for them to exit normally, if they don't then kill them
+ if (active_tasks.wait_for_exit(1))
+ active_tasks.kill_tasks();
+ // Check their final CPU time
+ active_tasks.check_apps();
+
return 0;
}
diff --git a/configure.in b/configure.in
index 930cf2ed2c..48ff2a5cd6 100644
--- a/configure.in
+++ b/configure.in
@@ -29,7 +29,7 @@ dnl Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
-AC_CHECK_HEADERS(fcntl.h malloc.h strings.h sys/time.h unistd.h sys/systeminfo.h sys/resource.h sys/types.h dirent.h sys/utsname.h netdb.h netinet/in.h arpa/inet.h signal.h sys/wait.h sys/file.h)
+AC_CHECK_HEADERS(fcntl.h malloc.h strings.h sys/time.h unistd.h sys/systeminfo.h sys/resource.h sys/types.h dirent.h sys/utsname.h netdb.h netinet/in.h arpa/inet.h signal.h sys/wait.h sys/file.h sys/ipc.h sys/shm.h)
AC_CHECK_HEADERS(mysql/include/mysql_com.h mysql/mysql_com.h)
dnl Checks for typedefs, structures, and compiler characteristics.
diff --git a/lib/msg_queue.h b/lib/msg_queue.h
index bfd206ee86..299b63cbbc 100644
--- a/lib/msg_queue.h
+++ b/lib/msg_queue.h
@@ -1,4 +1,12 @@
+#ifdef HAVE_SYS_TYPES_H
+#include
+#endif
+#ifdef HAVE_SYS_IPC_H
+#include
+#endif
+#ifdef HAVE_SYS_MSG_H
#include
+#endif
extern int create_message_queue(key_t);
extern int receive_message(key_t,void*,size_t,bool);
diff --git a/lib/shmem.C b/lib/shmem.C
index fc38372667..c18d55dc40 100755
--- a/lib/shmem.C
+++ b/lib/shmem.C
@@ -27,12 +27,14 @@
#include "shmem.h"
-int create_shmem(key_t key, int size, void** pp){
+int create_shmem(key_t key, int size, void** pp) {
int id;
+ char buf[256];
assert(pp!=NULL);
id = shmget(key, size, IPC_CREAT|0777);
if (id < 0) {
- perror("create_shmem: shmget");
+ sprintf(buf, "create_shmem: shmget: key: %x size: %d", (unsigned int)key, size);
+ perror(buf);
return -1;
}
return attach_shmem(key, pp);
@@ -64,17 +66,19 @@ int destroy_shmem(key_t key){
int attach_shmem(key_t key, void** pp){
void* p;
+ char buf[256];
int id;
assert(pp!=NULL);
- //fprintf(stderr, "%x\n", key);
id = shmget(key, 0, 0);
if (id < 0) {
- perror("attach_shmem: shmget");
+ sprintf(buf, "attach_shmem: shmget: key: %x mem_addr: %d", (unsigned int)key, (int)pp);
+ perror(buf);
return -1;
}
p = shmat(id, 0, 0);
if ((int)p == -1) {
- perror("attach_shmem: shmat");
+ sprintf(buf, "attach_shmem: shmat: key: %x mem_addr: %d", (unsigned int)key, (int)pp);
+ perror(buf);
return -1;
}
*pp = p;
@@ -88,3 +92,20 @@ int detach_shmem(void* p) {
if (retval) perror("detach_shmem: shmdt");
return retval;
}
+
+int shmem_info(key_t key) {
+ int id;
+ struct shmid_ds buf;
+ char buf2[256];
+
+ id = shmget(key, 0, 0);
+ if (id < 0) {
+ sprintf(buf2, "shmem_info: shmget: key: %x", (unsigned int)key);
+ perror(buf2);
+ return -1;
+ }
+ shmctl(id, IPC_STAT, &buf);
+ fprintf( stderr, "id: %d, size: %d, nattach: %d\n", id, buf.shm_segsz, buf.shm_nattch );
+
+ return 0;
+}
diff --git a/lib/shmem.h b/lib/shmem.h
index 7cd2641aed..5285f9a747 100755
--- a/lib/shmem.h
+++ b/lib/shmem.h
@@ -19,3 +19,5 @@ extern int attach_shmem(key_t, void**);
// detach from a shared-mem segment
//
extern int detach_shmem(void*);
+
+extern int shmem_info(key_t key);
diff --git a/todo b/todo
index e7fff6591c..82e276409a 100755
--- a/todo
+++ b/todo
@@ -4,7 +4,7 @@ BUGS (arranged from high to low priority)
- window closes and does not reopen when workunit finishes
and new workunit starts
- CPU time updates infrequently (every 10 seconds),
- should there be a user control for this?
+ add user control for this (HD write frequency)
- Client treats URL "maggie/ap/" different than URL "maggie/ap",
though this isn't really a bug it might be good to fix anyway
- global battery/user active prefs are always true in the client
@@ -17,8 +17,6 @@ HIGH-PRIORITY (should do for beta test)
- Implement Screensaver "blank screen" functionality
-multiple preference sets
-
implement server watchdogs
est_time_to_completion doesn't work for non-running tasks
@@ -37,6 +35,7 @@ THINGS TO TEST (preferably with test scripts)
- WU failure: too many errors
- WU failure: too many good results
- credit is granted even if result arrives very late
+- multiple preference sets
-----------------------
MEDIUM-PRIORITY (should do before public release)
-----------------------