- remote job submission: show 20 batches, with link to show all

- XML parser: make low-level functions inline, in an attempt
    (unsuccessful, as far as I can tell) to boost performance
This commit is contained in:
David Anderson 2012-11-07 00:08:18 -08:00 committed by Oliver Bock
parent 444cc65722
commit 6391a8c401
8 changed files with 354 additions and 240 deletions

View File

@ -6640,3 +6640,18 @@ Rom 5 Nov 2012
samples\vboxwrapper\
vboxwrapper.cpp
David 7 Nov 2012
- remote job submission: show 20 batches, with link to show all
- XML parser: make low-level functions inline, in an attempt
(unsuccessful, as far as I can tell) to boost performance
html/user/
submit.php
lib/
cc_config.h
parse.cpp,h
proxy_info.h
sched/
sched_main.cpp

View File

@ -36,7 +36,8 @@ function show_participant() {
function show_totals() {
$fn = "boinc_state.xml";
if (!file_exists($fn) || filemtime($fn) < time()-86400) {
$x = file_get_contents("http://boincstats.com/en/xml/boincState");
$uid = time();
$x = file_get_contents("http://boincstats.com/en/xml/boincState?uid=$uid");
if ($x) {
$f = fopen($fn, "w");
fwrite($f, $x);

View File

@ -27,34 +27,43 @@ error_reporting(E_ALL);
ini_set('display_errors', true);
ini_set('display_startup_errors', true);
// show a set of batches
//
function show_batches($batches) {
foreach ($batches as $batch) {
if ($batch->state < BATCH_STATE_COMPLETE || $batch->fraction_done < 1) {
$wus = BoincWorkunit::enum("batch = $batch->id");
$batch = get_batch_params($batch, $wus);
}
$app = BoincApp::lookup_id($batch->app_id);
if ($app) {
$batch->app_name = $app->name;
} else {
$batch->app_name = "unknown";
}
$user = BoincUser::lookup_id($batch->user_id);
if ($user) {
$batch->user_name = $user->name;
} else {
$batch->user_name = "missing user $batch->user_id";
}
}
define("PAGE_SIZE", 20);
function state_count($batches, $state) {
$n = 0;
foreach ($batches as $batch) {
if ($batch->state == $state) $n++;
}
return $n;
}
function show_all_link($batches, $state, $limit, $user, $app) {
$n = state_count($batches, $state);
if ($n > $limit) {
if ($user) $userid = $user->id;
else $userid = 0;
if ($app) $appid = $app->id;
else $appid = 0;
echo "Showing the most recent $limit of $n batches.
<a href=submit.php?action=show_all&state=$state&userid=$userid&appid=$appid>Show all $n</a>
";
}
}
function show_in_progress($batches, $limit, $user, $app) {
$first = true;
$n = 0;
foreach ($batches as $batch) {
if ($batch->state != BATCH_STATE_IN_PROGRESS) continue;
if ($limit && $n == $limit) break;
$n++;
if ($first) {
$first = false;
echo "<h2>Batches in progress</h2>\n";
if ($limit) {
show_all_link($batches, BATCH_STATE_IN_PROGRESS, $limit, $user, $app);
}
start_table();
table_header("name", "ID", "user", "app", "# jobs", "progress", "submitted");
}
@ -74,13 +83,21 @@ function show_batches($batches) {
} else {
end_table();
}
}
function show_complete($batches, $limit, $user, $app) {
$first = true;
$n = 0;
foreach ($batches as $batch) {
if ($batch->state != BATCH_STATE_COMPLETE) continue;
if ($limit && $n == $limit) break;
$n++;
if ($first) {
$first = false;
echo "<h2>Completed batches</h2>\n";
if ($limit) {
show_all_link($batches, BATCH_STATE_COMPLETE, $limit, $user, $app);
}
start_table();
table_header("name", "ID", "user", "app", "# jobs", "fraction done", "submitted");
}
@ -100,13 +117,21 @@ function show_batches($batches) {
} else {
end_table();
}
}
function show_aborted($batches, $limit, $user, $app) {
$first = true;
$n = 0;
foreach ($batches as $batch) {
if ($batch->state != BATCH_STATE_ABORTED) continue;
if ($limit && $n == $limit) break;
$n++;
if ($first) {
$first = false;
echo "<h2>Aborted batches</h2>\n";
if ($limit) {
show_all_link($batches, BATCH_STATE_ABORTED, $limit, $user, $app);
}
start_table();
table_header("name", "ID", "user", "app", "# jobs", "submitted");
}
@ -124,6 +149,38 @@ function show_batches($batches) {
}
}
// fill in the app and user names
//
function fill_in_app_and_user_names(&$batches) {
foreach ($batches as $batch) {
if ($batch->state < BATCH_STATE_COMPLETE || $batch->fraction_done < 1) {
$wus = BoincWorkunit::enum("batch = $batch->id");
$batch = get_batch_params($batch, $wus);
}
$app = BoincApp::lookup_id($batch->app_id);
if ($app) {
$batch->app_name = $app->name;
} else {
$batch->app_name = "unknown";
}
$user = BoincUser::lookup_id($batch->user_id);
if ($user) {
$batch->user_name = $user->name;
} else {
$batch->user_name = "missing user $batch->user_id";
}
}
}
// show a set of batches
//
function show_batches($batches, $limit, $user, $app) {
fill_in_app_and_user_names($batches);
show_in_progress($batches, $limit, $user, $app);
show_complete($batches, $limit, $user, $app);
show_aborted($batches, $limit, $user, $app);
}
// the job submission "home page":
// show the user's in-progress and completed batches,
// and a button for creating a new batch
@ -165,13 +222,12 @@ function handle_main($user) {
echo "</ul>";
$batches = BoincBatch::enum("user_id = $user->id order by id desc");
show_batches($batches);
show_batches($batches, PAGE_SIZE, $user, null);
page_tail();
}
function handle_admin($user) {
$app_id = get_int("app_id");
function check_admin_access($user, $app_id) {
$user_submit = BoincUserSubmit::lookup_userid($user->id);
if (!$user_submit) error_page("no access");
if ($app_id) {
@ -179,16 +235,24 @@ function handle_admin($user) {
$usa = BoincUserSubmitApp::lookup("user_id = $user->id and app_id=$app_id");
if (!$usa) error_page("no access");
}
} else {
if (!$user_submit->manage_all) error_page("no access");
}
}
function handle_admin($user) {
$app_id = get_int("app_id");
check_admin_access($user, $app_id);
if ($app_id) {
$app = BoincApp::lookup_id($app_id);
if (!$app) error_page("no such app");
page_head("Administer $app->user_friendly_name");
$batches = BoincBatch::enum("app_id = $app_id order by id desc");
show_batches($batches);
show_batches($batches, PAGE_SIZE, null, $app);
} else {
if (!$user_submit->manage_all) error_page("no access");
page_head("Administer all apps");
$batches = BoincBatch::enum("true order by id desc");
show_batches($batches);
show_batches($batches, PAGE_SIZE, null, null);
}
page_tail();
}
@ -412,6 +476,51 @@ function handle_retire_batch($user) {
page_tail();
}
function show_batches_in_state($batches, $state) {
switch ($state) {
case BATCH_STATE_IN_PROGRESS:
page_head("Batches in progress");
show_in_progress($batches, 0, null, null);
break;
case BATCH_STATE_COMPLETE:
page_head("Completed batches");
show_complete($batches, 0, null, null);
break;
case BATCH_STATE_ABORTED:
page_head("Aborted batches");
show_aborted($batches, 0, null, null);
break;
}
page_tail();
}
function handle_show_all($user) {
$userid = get_int("userid");
$appid = get_int("appid");
$state = get_int("state");
if ($userid) {
// user looking at their own batches
//
if ($userid != $user->id) error_page("wrong user");
$batches = BoincBatch::enum("user_id = $user->id and state=$state order by id desc");
fill_in_app_and_user_names($batches);
show_batches_in_state($batches, $state);
} else {
// admin looking at batches
//
check_admin_access($user, $appid);
if ($appid) {
$app = BoincApp::lookup_id($app_id);
if (!$app) error_page("no such app");
$batches = BoincBatch::enum("app_id = $app_id and state=$state order by id desc");
} else {
$batches = BoincBatch::enum("state=$state order by id desc");
}
fill_in_app_and_user_names($batches);
show_batches_in_state($batches, $state);
}
}
$user = get_logged_in_user();
$action = get_str('action', true);
@ -425,6 +534,7 @@ case 'query_batch': handle_query_batch($user); break;
case 'query_job': handle_query_job($user); break;
case 'retire_batch': handle_retire_batch($user); break;
case 'retire_batch_confirm': handle_retire_batch_confirm(); break;
case 'show_all': handle_show_all($user); break;
default:
error_page('no such action');
}

View File

@ -30,7 +30,7 @@
#include "proxy_info.h"
#include "coproc.h"
class XML_PARSER;
struct XML_PARSER;
#define MAX_FILE_XFERS_PER_PROJECT 2
#define MAX_FILE_XFERS 8

View File

@ -483,28 +483,6 @@ XML_PARSER::XML_PARSER(MIOFILE* _f) {
f = _f;
}
// read until find non-whitespace char.
// Return the char in the reference param
// Return true iff reached EOF
//
bool XML_PARSER::scan_nonws(int& first_char) {
char c;
while (1) {
c = f->_getc();
if (c == EOF) return true;
unsigned char uc = c;
if (isspace(uc)) continue;
first_char = c;
return false;
}
}
#define XML_PARSE_COMMENT 1
#define XML_PARSE_EOF 2
#define XML_PARSE_CDATA 3
#define XML_PARSE_TAG 4
#define XML_PARSE_DATA 5
int XML_PARSER::scan_comment() {
char buf[256];
char* p = buf;
@ -544,132 +522,6 @@ int XML_PARSER::scan_cdata(char* buf, int len) {
}
}
// we just read a <; read until we find a >.
// Given <tag [attr=val attr=val] [/]>:
// - copy tag (or tag/) to buf
// - copy "attr=val attr=val" to attr_buf
//
// Return either
// XML_PARSE_TAG
// XML_PARSE_COMMENT
// XML_PARSE_EOF
// XML_PARSE_CDATA
//
int XML_PARSER::scan_tag(
char* buf, int _tag_len, char* attr_buf, int attr_len
) {
int c;
char* buf_start = buf;
bool found_space = false;
int tag_len = _tag_len;
for (int i=0; ; i++) {
c = f->_getc();
if (c == EOF) return XML_PARSE_EOF;
if (c == '>') {
*buf = 0;
if (attr_buf) *attr_buf = 0;
return XML_PARSE_TAG;
}
if (isspace(c)) {
if (found_space && attr_buf) {
if (--attr_len > 0) {
*attr_buf++ = c;
}
}
found_space = true;
} else if (c == '/') {
if (--tag_len > 0) {
*buf++ = c;
}
} else {
if (found_space) {
if (attr_buf) {
if (--attr_len > 0) {
*attr_buf++ = c;
}
}
} else {
if (--tag_len > 0) {
*buf++ = c;
}
}
}
// check for comment start
//
if (i==2 && !strncmp(buf_start, "!--", 3)) {
return scan_comment();
}
if (i==7 && !strncmp(buf_start, "![CDATA[", 8)) {
return scan_cdata(buf_start, tag_len);
}
}
}
// read and copy text to buf; stop when find a <;
// ungetc() that so we read it again
// Return true iff reached EOF
//
bool XML_PARSER::copy_until_tag(char* buf, int len) {
int c;
while (1) {
c = f->_getc();
if (c == EOF) return true;
if (c == '<') {
f->_ungetc(c);
*buf = 0;
return false;
}
if (--len > 0) {
*buf++ = c;
}
}
}
// Scan something, either tag or text.
// Strip whitespace at start and end.
// Return true iff reached EOF
//
int XML_PARSER::get_aux(char* buf, int len, char* attr_buf, int attr_len) {
bool eof;
int c, retval;
while (1) {
eof = scan_nonws(c);
if (eof) return XML_PARSE_EOF;
if (c == '<') {
retval = scan_tag(buf, len, attr_buf, attr_len);
if (retval == XML_PARSE_EOF) return retval;
if (retval == XML_PARSE_COMMENT) continue;
} else {
buf[0] = c;
eof = copy_until_tag(buf+1, len-1);
if (eof) return XML_PARSE_EOF;
retval = XML_PARSE_DATA;
}
strip_whitespace(buf);
return retval;
}
}
bool XML_PARSER::get(
char* buf, int len, bool& _is_tag, char* attr_buf, int attr_len
) {
switch (get_aux(buf, len, attr_buf, attr_len)) {
case XML_PARSE_EOF: return true;
case XML_PARSE_TAG:
_is_tag = true;
break;
case XML_PARSE_DATA:
case XML_PARSE_CDATA:
default:
_is_tag = false;
break;
}
return false;
}
#define MAX_XML_STRING 262144
// We just parsed "parsed_tag".
@ -932,59 +784,6 @@ bool XML_PARSER::parse_start(const char* start_tag) {
return true;
}
// copy everything up to (but not including) the given end tag.
// The copied text may include XML tags.
// strips whitespace.
//
int XML_PARSER::element_contents(const char* end_tag, char* buf, int buflen) {
int n=0;
int retval=0;
while (1) {
if (n == buflen-1) {
retval = ERR_XML_PARSE;
break;
}
int c = f->_getc();
if (c == EOF) {
retval = ERR_XML_PARSE;
break;
}
buf[n++] = c;
buf[n] = 0;
char* p = strstr(buf, end_tag);
if (p) {
*p = 0;
break;
}
}
buf[n] = 0;
strip_whitespace(buf);
return retval;
}
#if 0
int XML_PARSER::element_contents(const char* end_tag, string& buf) {
int retval=0;
while (1) {
int c = f->_getc();
if (c == EOF) {
retval = ERR_XML_PARSE;
break;
}
buf += c;
char* p = strstr(buf.c_str(), end_tag);
if (p) {
int k = strlen(end_tag);
int n = buf.length();
buf.erase(n-k, k);
break;
}
}
strip_whitespace(buf);
return retval;
}
#endif
// We got an unexpected tag.
// If it's an end tag, do nothing.
// Otherwise skip until the end tag, if any

View File

@ -24,17 +24,20 @@
#include <errno.h>
#include "miofile.h"
#include "error_numbers.h"
#include "str_util.h"
// see parse_test.cpp for example usage of XML_PARSER
class XML_PARSER {
bool scan_nonws(int&);
#define XML_PARSE_COMMENT 1
#define XML_PARSE_EOF 2
#define XML_PARSE_CDATA 3
#define XML_PARSE_TAG 4
#define XML_PARSE_DATA 5
struct XML_PARSER {
int scan_comment();
int scan_tag(char*, int, char* ab=0, int al=0);
int scan_cdata(char*, int);
bool copy_until_tag(char*, int);
public:
char parsed_tag[4096];
bool is_tag;
MIOFILE* f;
@ -42,14 +45,187 @@ public:
void init(MIOFILE* mf) {
f = mf;
}
bool get(char*, int, bool&, char* ab=0, int al=0);
// read and copy text to buf; stop when find a <;
// ungetc() that so we read it again
// Return true iff reached EOF
//
inline bool copy_until_tag(char* buf, int len) {
int c;
while (1) {
c = f->_getc();
if (c == EOF) return true;
if (c == '<') {
f->_ungetc(c);
*buf = 0;
return false;
}
if (--len > 0) {
*buf++ = c;
}
}
}
inline bool get(
char* buf, int len, bool& _is_tag, char* attr_buf=0, int attr_len=0
) {
switch (get_aux(buf, len, attr_buf, attr_len)) {
case XML_PARSE_EOF: return true;
case XML_PARSE_TAG:
_is_tag = true;
break;
case XML_PARSE_DATA:
case XML_PARSE_CDATA:
default:
_is_tag = false;
break;
}
return false;
}
inline bool get_tag(char* ab=0, int al=0) {
return get(parsed_tag, sizeof(parsed_tag), is_tag, ab, al);
}
inline bool match_tag(const char* tag) {
return !strcmp(parsed_tag, tag);
}
int get_aux(char* buf, int len, char* attr_buf, int attr_len);
// read until find non-whitespace char.
// Return the char in the reference param
// Return true iff reached EOF
//
inline bool scan_nonws(int& first_char) {
char c;
while (1) {
c = f->_getc();
if (c == EOF) return true;
unsigned char uc = c;
if (isspace(uc)) continue;
first_char = c;
return false;
}
}
// Scan something, either tag or text.
// Strip whitespace at start and end.
// Return true iff reached EOF
//
inline int get_aux(
char* buf, int len, char* attr_buf, int attr_len
) {
bool eof;
int c, retval;
while (1) {
eof = scan_nonws(c);
if (eof) return XML_PARSE_EOF;
if (c == '<') {
retval = scan_tag(buf, len, attr_buf, attr_len);
if (retval == XML_PARSE_EOF) return retval;
if (retval == XML_PARSE_COMMENT) continue;
} else {
buf[0] = c;
eof = copy_until_tag(buf+1, len-1);
if (eof) return XML_PARSE_EOF;
retval = XML_PARSE_DATA;
}
strip_whitespace(buf);
return retval;
}
}
// we just read a <; read until we find a >.
// Given <tag [attr=val attr=val] [/]>:
// - copy tag (or tag/) to buf
// - copy "attr=val attr=val" to attr_buf
//
// Return either
// XML_PARSE_TAG
// XML_PARSE_COMMENT
// XML_PARSE_EOF
// XML_PARSE_CDATA
//
inline int scan_tag(
char* buf, int _tag_len, char* attr_buf=0, int attr_len=0
) {
int c;
char* buf_start = buf;
bool found_space = false;
int tag_len = _tag_len;
for (int i=0; ; i++) {
c = f->_getc();
if (c == EOF) return XML_PARSE_EOF;
if (c == '>') {
*buf = 0;
if (attr_buf) *attr_buf = 0;
return XML_PARSE_TAG;
}
if (isspace(c)) {
if (found_space && attr_buf) {
if (--attr_len > 0) {
*attr_buf++ = c;
}
}
found_space = true;
} else if (c == '/') {
if (--tag_len > 0) {
*buf++ = c;
}
} else {
if (found_space) {
if (attr_buf) {
if (--attr_len > 0) {
*attr_buf++ = c;
}
}
} else {
if (--tag_len > 0) {
*buf++ = c;
}
}
}
// check for comment start
//
if (i==2 && !strncmp(buf_start, "!--", 3)) {
return scan_comment();
}
if (i==7 && !strncmp(buf_start, "![CDATA[", 8)) {
return scan_cdata(buf_start, tag_len);
}
}
}
// copy everything up to (but not including) the given end tag.
// The copied text may include XML tags.
// strips whitespace.
//
inline int element_contents(const char* end_tag, char* buf, int buflen) {
int n=0;
int retval=0;
while (1) {
if (n == buflen-1) {
retval = ERR_XML_PARSE;
break;
}
int c = f->_getc();
if (c == EOF) {
retval = ERR_XML_PARSE;
break;
}
buf[n++] = c;
buf[n] = 0;
char* p = strstr(buf, end_tag);
if (p) {
*p = 0;
break;
}
}
buf[n] = 0;
strip_whitespace(buf);
return retval;
}
bool parse_start(const char*);
bool parse_str(const char*, char*, int);
bool parse_string(const char*, std::string&);
@ -58,8 +234,6 @@ public:
bool parse_ulong(const char*, unsigned long&);
bool parse_ulonglong(const char*, unsigned long long&);
bool parse_bool(const char*, bool&);
int element_contents(const char*, char*, int);
int element_contents(const char*, std::string&);
int copy_element(std::string&);
void skip_unexpected(const char*, bool verbose, const char*);
void skip_unexpected(bool verbose=false, const char* msg="") {

View File

@ -18,7 +18,7 @@
#ifndef _PROXY_INFO_
#define _PROXY_INFO_
class XML_PARSER;
struct XML_PARSER;
class MIOFILE;
// info on whether HTTP requests need to go through a proxy

View File

@ -349,6 +349,20 @@ inline static const char* get_remote_addr() {
return r ? r : "?.?.?.?";
}
#if 0 // performance test for XML parsing (use a large request)
int main(int, char**) {
SCHEDULER_REQUEST sreq;
FILE* f = fopen("req", "r");
MIOFILE mf;
XML_PARSER xp(&mf);
mf.init_file(f);
for (int i=0; i<10; i++) {
sreq.parse(xp);
fseek(f, 0, SEEK_SET);
}
}
#else
#if !defined(PLAN_CLASS_TEST)
int main(int argc, char** argv) {
@ -651,6 +665,7 @@ done:
}
}
#endif
#endif
// the following stuff is here because if you put it in sched_limit.cpp
// you get "ssp undefined" in programs other than cgi