diff --git a/checkin_notes b/checkin_notes
index de218af72e..630fe44239 100644
--- a/checkin_notes
+++ b/checkin_notes
@@ -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
+
diff --git a/doc/index.php b/doc/index.php
index 8c92b149b8..3a0e000bdd 100644
--- a/doc/index.php
+++ b/doc/index.php
@@ -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);
diff --git a/html/user/submit.php b/html/user/submit.php
index 03599abe9c..dfec3efce1 100644
--- a/html/user/submit.php
+++ b/html/user/submit.php
@@ -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.
+ Show all $n
+ ";
+ }
+}
+
+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 "
Batches in progress
\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 "Completed batches
\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 "Aborted batches
\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 "";
$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');
}
diff --git a/lib/cc_config.h b/lib/cc_config.h
index 59805980c1..e4263c9403 100644
--- a/lib/cc_config.h
+++ b/lib/cc_config.h
@@ -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
diff --git a/lib/parse.cpp b/lib/parse.cpp
index b64b337780..824975a717 100644
--- a/lib/parse.cpp
+++ b/lib/parse.cpp
@@ -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 :
-// - 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
diff --git a/lib/parse.h b/lib/parse.h
index c7d67ff0f0..731e393da3 100644
--- a/lib/parse.h
+++ b/lib/parse.h
@@ -24,17 +24,20 @@
#include
#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 :
+ // - 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="") {
diff --git a/lib/proxy_info.h b/lib/proxy_info.h
index 53a2210291..3525c7cb0d 100644
--- a/lib/proxy_info.h
+++ b/lib/proxy_info.h
@@ -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
diff --git a/sched/sched_main.cpp b/sched/sched_main.cpp
index 90bd725250..3ce90524ab 100644
--- a/sched/sched_main.cpp
+++ b/sched/sched_main.cpp
@@ -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