From 1e631545742ccf71ee57d5befeb6e3d09acc90d4 Mon Sep 17 00:00:00 2001 From: Baoshuo Date: Tue, 8 Nov 2022 07:06:49 +0800 Subject: [PATCH 1/3] feat(judger): support non-integer time limit --- judger/uoj_judger/include/uoj_judger.h | 21 +++++++++++++++++++-- judger/uoj_judger/include/uoj_judger_v2.h | 21 +++++++++++++++++++-- judger/uoj_judger/include/uoj_run.h | 6 +++--- judger/uoj_judger/run/run_program.cpp | 14 ++++++++++---- judger/uoj_judger/run/run_program_sandbox.h | 1 + 5 files changed, 52 insertions(+), 11 deletions(-) diff --git a/judger/uoj_judger/include/uoj_judger.h b/judger/uoj_judger/include/uoj_judger.h index 0dda40c..4468ded 100644 --- a/judger/uoj_judger/include/uoj_judger.h +++ b/judger/uoj_judger/include/uoj_judger.h @@ -633,6 +633,23 @@ int conf_int(const string &key, int num, const int &val) { int conf_int(const string &key) { return conf_int(key, 0); } +double conf_double(const string &key, const double &val) { + if (config.count(key) == 0) { + return val; + } + return stod(config[key]); +} +double conf_double(const string &key, int num, const double &val) { + ostringstream sout; + sout << key << "_" << num; + if (config.count(sout.str()) == 0) { + return conf_double(key, val); + } + return stod(config[sout.str()]); +} +double conf_double(const string &key) { + return conf_double(key, 0); +} string conf_file_name_with_num(string s, int num) { ostringstream name; if (num < 0) { @@ -664,10 +681,10 @@ runp::limits_t conf_run_limit(string pre, const int &num, const runp::limits_t & pre += "_"; } runp::limits_t limits; - limits.time = conf_int(pre + "time_limit", num, val.time); + limits.time = conf_double(pre + "time_limit", num, val.time); limits.memory = conf_int(pre + "memory_limit", num, val.memory); limits.output = conf_int(pre + "output_limit", num, val.output); - limits.real_time = conf_int(pre + "real_time_limit", num, val.real_time); + limits.real_time = conf_double(pre + "real_time_limit", num, val.real_time); limits.stack = conf_int(pre + "stack_limit", num, val.stack); return limits; } diff --git a/judger/uoj_judger/include/uoj_judger_v2.h b/judger/uoj_judger/include/uoj_judger_v2.h index c6277f5..1aaf077 100644 --- a/judger/uoj_judger/include/uoj_judger_v2.h +++ b/judger/uoj_judger/include/uoj_judger_v2.h @@ -501,6 +501,23 @@ int conf_int(const string &key, int num, const int &val) { int conf_int(const string &key) { return conf_int(key, 0); } +double conf_double(const string &key, const double &val) { + if (uconfig.count(key) == 0) { + return val; + } + return stod(uconfig[key]); +} +double conf_double(const string &key, int num, const double &val) { + ostringstream sout; + sout << key << "_" << num; + if (uconfig.count(sout.str()) == 0) { + return conf_double(key, val); + } + return stod(uconfig[sout.str()]); +} +double conf_double(const string &key) { + return conf_double(key, 0); +} string conf_file_name_with_num(string s, int num) { ostringstream name; if (num < 0) { @@ -530,10 +547,10 @@ runp::limits_t conf_run_limit(string pre, const int &num, const runp::limits_t & pre += "_"; } runp::limits_t limits; - limits.time = conf_int(pre + "time_limit", num, val.time); + limits.time = conf_double(pre + "time_limit", num, val.time); limits.memory = conf_int(pre + "memory_limit", num, val.memory); limits.output = conf_int(pre + "output_limit", num, val.output); - limits.real_time = conf_int(pre + "real_time_limit", num, val.real_time); + limits.real_time = conf_double(pre + "real_time_limit", num, val.real_time); limits.stack = conf_int(pre + "stack_limit", num, val.real_time); return limits; } diff --git a/judger/uoj_judger/include/uoj_run.h b/judger/uoj_judger/include/uoj_run.h index c2f35b1..25783b0 100644 --- a/judger/uoj_judger/include/uoj_run.h +++ b/judger/uoj_judger/include/uoj_run.h @@ -133,14 +133,14 @@ namespace runp { fs::path run_path; struct limits_t { - int time; + double time; int memory; int output; - int real_time; + double real_time; int stack; limits_t() = default; - limits_t(const int &_time, const int &_memory, const int &_output) + limits_t(const double &_time, const int &_memory, const int &_output) : time(_time), memory(_memory), output(_output), real_time(-1), stack(-1) { } }; diff --git a/judger/uoj_judger/run/run_program.cpp b/judger/uoj_judger/run/run_program.cpp index d6135dc..59309a1 100644 --- a/judger/uoj_judger/run/run_program.cpp +++ b/judger/uoj_judger/run/run_program.cpp @@ -51,10 +51,10 @@ error_t run_program_argp_parse_opt (int key, char *arg, struct argp_state *state switch (key) { case 'T': - config->limits.time = atoi(arg); + config->limits.time = stod(arg); break; case 'R': - config->limits.real_time = atoi(arg); + config->limits.real_time = stod(arg); break; case 'M': config->limits.memory = atoi(arg); @@ -201,9 +201,15 @@ void set_limit(int r, int rcur, int rmax = -1) { } } -void set_user_cpu_time_limit(int tl) { +void set_user_cpu_time_limit(double tl) { struct itimerval val; - val.it_value = {tl, 100 * 1000}; + long tl_sec = (long)tl; + long tl_usec = (long)((tl - floor(tl)) * 1000 + 100) * 1000; + if (tl_usec >= 1'000'000l) { + tl_sec++; + tl_usec -= 1'000'000l; + } + val.it_value = {tl_sec, tl_usec}; val.it_interval = {0, 100 * 1000}; setitimer(ITIMER_VIRTUAL, &val, NULL); } diff --git a/judger/uoj_judger/run/run_program_sandbox.h b/judger/uoj_judger/run/run_program_sandbox.h index 27dbfd4..7f47639 100644 --- a/judger/uoj_judger/run/run_program_sandbox.h +++ b/judger/uoj_judger/run/run_program_sandbox.h @@ -1,5 +1,6 @@ #include #include +#include #include #include #include From d7aea4cbbc77f5c2b1a9a2a062a3e6acae78f894 Mon Sep 17 00:00:00 2001 From: Baoshuo Date: Tue, 8 Nov 2022 15:24:21 +0800 Subject: [PATCH 2/3] chore(judger): add runp::double_to_itimerval --- judger/uoj_judger/include/uoj_run.h | 19 +++++++++++++++++++ judger/uoj_judger/run/run_program.cpp | 10 +--------- judger/uoj_judger/run/run_program_sandbox.h | 2 -- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/judger/uoj_judger/include/uoj_run.h b/judger/uoj_judger/include/uoj_run.h index 25783b0..2316656 100644 --- a/judger/uoj_judger/include/uoj_run.h +++ b/judger/uoj_judger/include/uoj_run.h @@ -1,3 +1,4 @@ +#include #include #include #include @@ -7,6 +8,7 @@ #include #include #include +#include #define UOJ_GCC "/usr/bin/gcc-11" #define UOJ_GPLUSPLUS "/usr/bin/g++-11" @@ -388,6 +390,23 @@ namespace runp { } } }; + + itimerval double_to_itimerval(const double &tl) { + struct itimerval val; + + long tl_sec = (long)tl; + long tl_usec = (long)((tl - floor(tl)) * 1000 + 100) * 1000; + + if (tl_usec >= 1'000'000l) { + tl_sec++; + tl_usec -= 1'000'000l; + } + + val.it_value = {tl_sec, tl_usec}; + val.it_interval = {0, 100 * 1000}; + + return val; + } } namespace runp::interaction { diff --git a/judger/uoj_judger/run/run_program.cpp b/judger/uoj_judger/run/run_program.cpp index 59309a1..567f97b 100644 --- a/judger/uoj_judger/run/run_program.cpp +++ b/judger/uoj_judger/run/run_program.cpp @@ -202,15 +202,7 @@ void set_limit(int r, int rcur, int rmax = -1) { } void set_user_cpu_time_limit(double tl) { - struct itimerval val; - long tl_sec = (long)tl; - long tl_usec = (long)((tl - floor(tl)) * 1000 + 100) * 1000; - if (tl_usec >= 1'000'000l) { - tl_sec++; - tl_usec -= 1'000'000l; - } - val.it_value = {tl_sec, tl_usec}; - val.it_interval = {0, 100 * 1000}; + itimerval val = runp::double_to_itimerval(tl); setitimer(ITIMER_VIRTUAL, &val, NULL); } diff --git a/judger/uoj_judger/run/run_program_sandbox.h b/judger/uoj_judger/run/run_program_sandbox.h index 7f47639..ff9ea6a 100644 --- a/judger/uoj_judger/run/run_program_sandbox.h +++ b/judger/uoj_judger/run/run_program_sandbox.h @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -15,7 +14,6 @@ #include #include #include -#include #include #include #include From 2e53a2733f81d815ddc867bbeaf18e92cbaf4989 Mon Sep 17 00:00:00 2001 From: Baoshuo Date: Mon, 28 Nov 2022 17:33:11 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix(judger):=20=E5=AE=8C=E5=96=84=E6=B5=AE?= =?UTF-8?q?=E7=82=B9=E6=95=B0=E6=97=B6=E9=99=90=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref: https://github.com/vfleaking/uoj/pull/100/commits/986c7aa4edc5bae87c9833a527e091111bfe1043 Co-authored-by: Kaifeng Lyu --- judger/uoj_judger/include/uoj_run.h | 42 ++++++++++++++++++--------- judger/uoj_judger/run/run_program.cpp | 26 ++++++++++++----- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/judger/uoj_judger/include/uoj_run.h b/judger/uoj_judger/include/uoj_run.h index 2316656..4cc9047 100644 --- a/judger/uoj_judger/include/uoj_run.h +++ b/judger/uoj_judger/include/uoj_run.h @@ -22,6 +22,11 @@ std::string escapeshellarg(int arg) { return std::to_string(arg); } +std::string escapeshellarg(double arg) { + std::ostringstream sout; + sout << std::setprecision(15) << arg; + return sout.str(); +} std::string escapeshellarg(const std::string &arg) { std::string res = "'"; for (char c : arg) { @@ -391,21 +396,30 @@ namespace runp { } }; - itimerval double_to_itimerval(const double &tl) { - struct itimerval val; + /** + * @brief convert a time t to timeval. Assume t <= 1000. Accurate to ms. + * + * @param t time in double + * @return timeval + */ + timeval double_to_timeval(const double &t) { + long tl = round(t * 1000); + long tl_sec = tl / 1000; + long tl_usec = tl % 1000 * 1000; + return {tl_sec, tl_usec}; + } - long tl_sec = (long)tl; - long tl_usec = (long)((tl - floor(tl)) * 1000 + 100) * 1000; - - if (tl_usec >= 1'000'000l) { - tl_sec++; - tl_usec -= 1'000'000l; - } - - val.it_value = {tl_sec, tl_usec}; - val.it_interval = {0, 100 * 1000}; - - return val; + /** + * @brief convert a time t to timespec. Assume t <= 1000. Accurate to ms. + * + * @param t time in double + * @return timespec + */ + timespec double_to_timespec(const double &t) { + long tl = round(t * 1000); + long tl_sec = tl / 1000; + long tl_nsec = tl % 1000 * 1'000'000; + return {tl_sec, tl_nsec}; } } diff --git a/judger/uoj_judger/run/run_program.cpp b/judger/uoj_judger/run/run_program.cpp index 567f97b..e39c936 100644 --- a/judger/uoj_judger/run/run_program.cpp +++ b/judger/uoj_judger/run/run_program.cpp @@ -51,10 +51,10 @@ error_t run_program_argp_parse_opt (int key, char *arg, struct argp_state *state switch (key) { case 'T': - config->limits.time = stod(arg); + config->limits.time = round(stod(arg) * 1000) / 1000; break; case 'R': - config->limits.real_time = stod(arg); + config->limits.real_time = round(stod(arg) * 1000) / 1000; break; case 'M': config->limits.memory = atoi(arg); @@ -202,7 +202,16 @@ void set_limit(int r, int rcur, int rmax = -1) { } void set_user_cpu_time_limit(double tl) { - itimerval val = runp::double_to_itimerval(tl); + itimerval val; + val.it_value = runp::double_to_timeval(tl); + val.it_interval = {0, 100'000}; + + val.it_value.tv_usec += 100'000; + if (val.it_value.tv_usec >= 1'000'000) { + val.it_value.tv_sec++; + val.it_value.tv_usec -= 1'000'000; + } + setitimer(ITIMER_VIRTUAL, &val, NULL); } @@ -302,7 +311,7 @@ struct rusage *ruse0p = NULL; bool has_real_TLE() { struct timeval elapsed; timersub(&end_time, &start_time, &elapsed); - return elapsed.tv_sec >= run_program_config.limits.real_time; + return elapsed.tv_sec + elapsed.tv_usec / 1'000'000. >= run_program_config.limits.real_time; } int rp_children_pos(pid_t pid) { @@ -628,9 +637,12 @@ void dispatch_event(run_event&& e) { if (rp_timer_pid == -1) { runp::result(runp::RS_JGF, "error code: FKFAL2").dump_and_exit(); // fork failed } else if (rp_timer_pid == 0) { - struct timespec ts; - ts.tv_sec = run_program_config.limits.real_time; - ts.tv_nsec = 100 * 1000000; + struct timespec ts = runp::double_to_timespec(run_program_config.limits.real_time); + ts.tv_nsec += 100'000'000; + if (ts.tv_nsec >= 1'000'000'000) { + ts.tv_sec += 1; + ts.tv_nsec -= 1'000'000'000; + } nanosleep(&ts, NULL); exit(0); }