- API, Unix: call getrusage() from the timer thread,

not the worker signal handler.
    There's no reason to call it from the signal handler -
    it returns the CPU for the entire process, not the calling thread.
    And it may be asynch-signal-handler-unsafe.
- API: comment out checks for bad CPU times.
    I don't think this is needed now, and in some cases it's wrong
    (multi-threaded apps can accumulate CPU faster than real time)
- API, Unix: in boinc_calling_thread_cpu_time(), don't retry getrusage().
- Bossa: switch to better class structure (suggested by Nicolas Alvarez).
    Haven't switched to mysqli yet, but will later.
    Also various other Bossa fixes

svn path=/trunk/boinc/; revision=13855
This commit is contained in:
David Anderson 2007-10-16 23:02:13 +00:00
parent 92509b6d4b
commit b079d40cb9
10 changed files with 208 additions and 66 deletions

View File

@ -191,15 +191,10 @@ static int setup_shared_mem() {
}
// Return CPU time of worker thread (and optionally others)
// This may be called from other threads
// This may be called from any thread
//
double boinc_worker_thread_cpu_time() {
static double last_cpu=0;
// last value returned by this func
static time_t last_time=0;
// when it was returned
time_t now = time(0);
double cpu, time_diff = (double)(now - last_time);
double cpu;
#ifdef _WIN32
int retval;
if (options.all_threads_cpu_time) {
@ -216,6 +211,18 @@ double boinc_worker_thread_cpu_time() {
cpu += (double)worker_thread_ru.ru_stime.tv_sec
+ (((double)worker_thread_ru.ru_stime.tv_usec)/1000000.0);
#endif
#if 0
// The following paranoia is (I hope) not needed anymore.
// In any case, the check for CPU incrementing faster than real time
// is misguided - it assumes no multi-threading.
//
static double last_cpu=0;
// last value returned by this func
static time_t last_time=0;
// when it was returned
time_t now = time(0);
double time_diff = (double)(now - last_time);
if (!finite(cpu)) {
fprintf(stderr, "CPU time infinite or NaN\n");
last_time = now;
@ -228,11 +235,12 @@ double boinc_worker_thread_cpu_time() {
return last_cpu;
}
if (cpu_diff>(time_diff + 1)) {
// fprintf(stderr, "CPU time incrementing faster than real time. Correcting.\n");
fprintf(stderr, "CPU time incrementing faster than real time. Correcting.\n");
cpu = last_cpu + time_diff + 1; // allow catch-up
}
last_cpu = cpu;
last_time = now;
#endif
return cpu;
}
@ -923,6 +931,7 @@ void* timer_thread(void*) {
block_sigalrm();
while(1) {
boinc_sleep(TIMER_PERIOD);
getrusage(RUSAGE_SELF, &worker_thread_ru);
timer_handler();
}
return 0;
@ -933,7 +942,6 @@ void* timer_thread(void*) {
// It must call only signal-safe functions, and must not do FP math
//
void worker_signal_handler(int) {
getrusage(RUSAGE_SELF, &worker_thread_ru);
if (options.direct_process_action) {
while (boinc_status.suspended && !in_critical_section) {
sleep(1); // don't use boinc_sleep() because it does FP math

View File

@ -9556,3 +9556,34 @@ Rom 16 Oct 2007
boinc_tray.h (Added)
boinc_tray.rc (Added)
tray_win.cpp, .h (Added)
David 16 Oct 2007
- API, Unix: call getrusage() from the timer thread,
not the worker signal handler.
There's no reason to call it from the signal handler -
it returns the CPU for the entire process, not the calling thread.
And it may be asynch-signal-handler-unsafe.
- API: comment out checks for bad CPU times.
I don't think this is needed now, and in some cases it's wrong
(multi-threaded apps can accumulate CPU faster than real time)
- API, Unix: in boinc_calling_thread_cpu_time(), don't retry getrusage().
- Bossa: switch to better class structure (suggested by Nicolas Alvarez).
Haven't switched to mysqli yet, but will later.
Also various other Bossa fixes
api/
boinc_api.C
db/
bossa_constraints.sal
bossa_schema.sql
html/
inc/
bossa_db.inc
ops/
bossa_make_jobs_example.php
bossa_setup_example.php
user/
bossa_example.php (new)
bossa_get_job.php
lib/
util.C

View File

@ -2,5 +2,9 @@ alter table bossa_app
add unique(name);
alter table bossa_job
add unique(name)
add index bj_more_needed(more_needed);
add unique(name),
add index bj_more_needed(app, more_needed);
alter table bossa_job_inst
add index bji_job(job_id),
add index bji_user(user_id);

View File

@ -5,7 +5,6 @@ create table bossa_app (
user_friendly_name varchar(255) not null,
long_jobs tinyint not null,
start_url varchar(255) not null,
finish_url varchar(255) not null,
deprecated tinyint not null,
beta tinyint not null,
primary key(id)
@ -33,5 +32,6 @@ create table bossa_job_inst (
job_id integer not null,
user_id integer not null,
finish_time integer not null,
info varchar(255) not null,
primary key(id)
);

View File

@ -1,18 +1,25 @@
<?php
class Bossa {
function insert_app(&$app) {
if (!$app->long_jobs) $app->long_jobs = 0;
function bossa_lookup($id, $table) {
$result = mysql_query("select * from $table where id='$id'");
if (!$result) return null;
$obj = mysql_fetch_object($result);
mysql_free_result($result);
return $obj;
}
class BossaApp {
function insert() {
if (!$this->long_jobs) $this->long_jobs = 0;
$now = time();
$query = "insert into bossa_app (create_time, name, user_friendly_name, long_jobs, start_url, finish_url) values ($now, '$app->name', '$app->user_friendly_name', $app->long_jobs, '$app->start_url', '$app->finish_url')";
echo $query;
$query = "insert into bossa_app (create_time, name, user_friendly_name, long_jobs, start_url) values ($now, '$this->name', '$this->user_friendly_name', $this->long_jobs, '$this->start_url')";
$result = mysql_query($query);
if (!$result) return false;
$app->id = mysql_insert_id();
$this->id = mysql_insert_id();
return true;
}
function app_lookup_name($name) {
static function lookup_name($name) {
$result = mysql_query("select * from bossa_app where name='$name'");
if (!$result) return null;
$app = mysql_fetch_object($result);
@ -20,42 +27,56 @@ class Bossa {
return $app;
}
function app_lookup_id($id) {
$result = mysql_query("select * from bossa_app where id='$id'");
if (!$result) return null;
$app = mysql_fetch_object($result);
mysql_free_result($result);
return $app;
static function lookup_id($id) {
return bossa_lookup($id, 'bossa_app');
}
}
function insert_job(&$job) {
class BossaJob {
function insert() {
$now = time();
$query = "insert into bossa_job (create_time, name, app_id, info, batch, time_estimate, time_limit, more_needed, npending, nsuccess, nsuccess_needed) values ($now, '$job->name', $job->app_id, '$job->info', $job->batch, $job->time_estimate, $job->time_limit, 1, 0, 0, $job->nsuccess_needed)";
$query = "insert into bossa_job (create_time, name, app_id, info, batch, time_estimate, time_limit, more_needed, npending, nsuccess, nsuccess_needed) values ($now, '$this->name', $this->app_id, '$this->info', $this->batch, $this->time_estimate, $this->time_limit, 1, 0, 0, $this->nsuccess_needed)";
$result = mysql_query($query);
if (!$result) {
echo "$query\n";
return false;
}
$job->id = mysql_insert_id();
$this->id = mysql_insert_id();
return true;
}
function update($clause) {
return mysql_query("update bossa_job set $clause where id=$this->id");
}
static function lookup_id($id) {
return bossa_lookup($id, 'bossa_job');
}
}
function insert_job_inst(&$ji) {
class BossaJobInst {
function insert() {
$now = time();
$query = "insert into bossa_job_inst (create_time, job_id, user_id) values ($now, $ji->job_id, $ji->user_id)";
$query = "insert into bossa_job_inst (create_time, job_id, user_id) values ($now, $this->job_id, $this->user_id)";
$result = mysql_query($query);
if (!$result) {
echo "$query\n";
return false;
}
$ji->id = mysql_insert_id();
$this->id = mysql_insert_id();
return true;
}
static function lookup_id($id) {
return bossa_lookup($id, 'bossa_job_inst');
}
function update($clause) {
return mysql_query("update bossa_job_inst set $clause where id=$this->id");
}
// Assign a job from the given app to the given user.
// Returns the job instance or NULL.
//
function assign_job($app, $user) {
static function assign($app, $user) {
// this query skips jobs for which this user
// has already been assigned an instance
//
@ -69,15 +90,36 @@ class Bossa {
echo "<hr>";
print_r($job);
echo "<hr>";
$ji = new BossaJobInst();
$ji->user_id = $user->id;
$ji->job_id = $job->id;
if (!Bossa::insert_job_inst($ji)) {
if (!$ji->insert()) {
echo mysql_error();
return null;
}
return $ji;
}
// The given job instance has completed
//
function completed($job) {
$this->finish_time = time();
$this->update("finish_time=$this->finish_time, info='$this->info'");
$job->npending--;
$job->nsuccess++;
$job->more_needed = ($job->npending+$job->nsuccess < $job->nsuccess_needed);
return BossaJob::update("npending=$job->npending, nsuccess=$job->nsuccess, more_needed=$job->more_needed");
}
// The given job instance has timed out.
//
function timed_out($job) {
$job->npending--;
$job->more_needed = ($job->npending+$job->nsuccess < $job->nsuccess_needed);
return BossaJob::update("npending=$job->npending, more_needed=$job->more_needed");
}
}
?>

View File

@ -1,31 +1,35 @@
<?php
require_once("../bossa_inc/bossa_db.inc");
require_once("../inc/bossa_db.inc");
require_once("../inc/db.inc");
db_init();
function make_jobs() {
$app = Bossa::app_lookup_name('bossa_test');
$appname = 'bossa_test';
$app = BossaApp::lookup_name($appname);
if (!$app) {
echo "No app\n";
echo "Application $appname not found\n";
exit(1);
}
$job = new BossaJob;
$job->app_id = $app->id;
$job->batch = 0;
$job->time_estimate = 30;
$job->time_limit = 600;
$job->nsuccess_needed = 3;
for ($i=0; $i<10; $i++) {
$j = $i % 2;
$job->name = "job_$i";
$job->job_info = "$i";
if (!Bossa::insert_job($job)) {
echo "failed: ", mysql_error();
$job->info = "$j";
if (!$job->insert()) {
echo "BossaJob::insert failed: ", mysql_error(), "\n";
exit(1);
}
}
}
make_jobs();
echo "All done.\n";
?>

View File

@ -1,6 +1,6 @@
<?php
require_once("../bossa_inc/bossa_db.inc");
require_once("../inc/bossa_db.inc");
require_once("../inc/db.inc");
db_init();
@ -8,14 +8,15 @@ db_init();
// Set up Bossa applications.
// Customize and rename this file.
$ba = new BossaApp();
$ba->name = 'bossa_test';
$ba->user_friendly_name = 'Simple pattern recognition';
$ba->start_url = 'test_start.php';
$ba->start_url = 'bossa_example.php';
if (Bossa::insert_app($ba)) {
echo "success\n";
if ($ba->insert($ba)) {
echo "Added application '$ba->name'\n";
} else {
echo "failed ", mysql_error();
echo "Couldn't add '$ba->name': ", mysql_error(), "\n";
}
?>

View File

@ -0,0 +1,62 @@
<?php
require_once("../inc/util.inc");
require_once("../inc/bossa_db.inc");
db_init();
// Bossa example.
// Show the user an image and ask them whether it's a zero or one.
function show_job($bj, $bji) {
if ($bji->finish_time) {
error_page("You already finished this job");
}
$i = $bj->info;
$img_url = "http://boinc.berkeley.edu/images/number_$i.jpg";
echo "
<form method=get action=bossa_example.php>
<input type=hidden name=bji value=$bji->id>
<img src=$img_url>
<br>
The picture shows a
<br><input type=radio name=response value=0> zero
<br><input type=radio name=response value=1> one
<br><input type=radio name=response value=2 checked> not sure
<br><br><input type=submit name=submit value=OK>
</form>
";
}
function handle_job_completion($bj, $bji) {
$response = get_int('response');
print_r($bji);
$bji->info = "response=$response";
$bji->completed($bj);
// show another job immediately
//
$url = "bossa_get_job.php?bossa_app_id=$bj->app_id";
Header("Location: $url");
}
$user = get_logged_in_user();
$bji = BossaJobInst::lookup_id(get_int('bji'));
if (!$bji) {
error_page("No such job instance");
}
if ($bji->user_id != $user->id) {
error_page("Bad user ID");
}
$bj = BossaJob::lookup_id($bji->job_id);
if (!$bj) {
error_page("No such job");
}
if ($_GET['submit']) {
handle_job_completion($bj, $bji);
} else {
show_job($bj, $bji);
}
?>

View File

@ -2,27 +2,27 @@
require_once("../inc/util.inc");
require_once("../inc/db.inc");
require_once("../bossa_inc/bossa_db.inc");
require_once("../inc/bossa_db.inc");
db_init();
$user = get_logged_in_user();
$bossa_app_id = get_int('bossa_app_id');
$app = Bossa::app_lookup_id($bossa_app_id);
$app = BossaApp::lookup_id(get_int('bossa_app_id'));
if (!$app) {
error_page("no such app: $bossa_app_id");
}
$ji = Bossa::assign_job($app, $user);
$ji = BossaJobInst::assign($app, $user);
if ($ji) {
$url = $app->start_url."&job_info=".$job->job_info;
$url = $app->start_url."?bji=$ji->id";
Header("Location: $url");
} else {
page_head("No jobs available");
echo "
Sorry, no jobs are available right not.
Sorry, no more jobs are available right now.
<p>
Please try again later.
";
page_tail();

View File

@ -191,26 +191,16 @@ int boinc_calling_thread_cpu_time(double& cpu) {
#else
// Unix: pthreads doesn't seem to provide an API for getting
// per-thread CPU time. So just get the process's CPU time
// Unix: pthreads doesn't provide an API for getting per-thread CPU time,
// so just get the process's CPU time
//
int boinc_calling_thread_cpu_time(double &cpu_t) {
int retval=1;
struct rusage ru;
// getrusage can return an error, so try a few times if it returns an error.
//
for (int i=0; i<10; i++) {
retval = getrusage(RUSAGE_SELF, &ru);
if (!retval) break;
}
if (retval) {
return ERR_GETRUSAGE;
}
// Sum the user and system time
//
cpu_t = (double)ru.ru_utime.tv_sec + (((double)ru.ru_utime.tv_usec) / ((double)1000000.0));
cpu_t += (double)ru.ru_stime.tv_sec + (((double)ru.ru_stime.tv_usec) / ((double)1000000.0));
int retval = getrusage(RUSAGE_SELF, &ru);
if (retval) return ERR_GETRUSAGE;
cpu_t = (double)ru.ru_utime.tv_sec + ((double)ru.ru_utime.tv_usec) / 1e6;
cpu_t += (double)ru.ru_stime.tv_sec + ((double)ru.ru_stime.tv_usec) / 1e6;
return 0;
}