From bb105d1451e414df6020ebad13b77cfef7e40736 Mon Sep 17 00:00:00 2001 From: Paul Pan Date: Sat, 6 Jan 2024 19:21:37 +0800 Subject: [PATCH] feat: #6 [7] Rewrite run_judge --- internal/api/runner/judge.go | 10 +- internal/service/runner/common.go | 42 +++--- internal/service/runner/compile.go | 52 ++----- internal/service/runner/config.go | 16 +- internal/service/runner/new_problem.go | 4 +- internal/service/runner/runAndJudge.go | 22 --- internal/service/runner/run_judge.go | 183 +++++++++++++++++++++++ internal/service/runner/service.go | 15 +- pkg/utils/must.go | 5 + resource/runner/scripts/docker_run.sh | 21 --- resource/runner/scripts/problem.sh | 76 ---------- resource/runner/scripts/problem_judge.sh | 40 ----- resource/runner/scripts/problem_run.sh | 72 --------- 13 files changed, 244 insertions(+), 314 deletions(-) delete mode 100644 internal/service/runner/runAndJudge.go create mode 100644 internal/service/runner/run_judge.go delete mode 100755 resource/runner/scripts/docker_run.sh delete mode 100755 resource/runner/scripts/problem.sh delete mode 100755 resource/runner/scripts/problem_judge.sh delete mode 100755 resource/runner/scripts/problem_run.sh diff --git a/internal/api/runner/judge.go b/internal/api/runner/judge.go index 113c5a5..2700bc4 100644 --- a/internal/api/runner/judge.go +++ b/internal/api/runner/judge.go @@ -56,14 +56,8 @@ func (h *handler) Judge(_ context.Context, t *asynq.Task) error { return e.Success, 0, compileResult } - // 4. config - config, err := h.runnerService.ParseConfig(p.ProblemVersionID, true) - if err != nil { - return e.InternalError, 0, systemError - } - - // 5. run and judge - result, point, status := h.runnerService.RunAndJudge(p.ProblemVersionID, user, p.Submission.Language, &config) + // 4. run and judge + result, point, status := h.runnerService.RunAndJudge(p.ProblemVersionID, user, p.Submission.Language) return utils.If(status != e.Success, e.InternalError, e.Success), point, result }() diff --git a/internal/service/runner/common.go b/internal/service/runner/common.go index 942332b..b225a87 100644 --- a/internal/service/runner/common.go +++ b/internal/service/runner/common.go @@ -30,31 +30,6 @@ func init() { TmpDir = path.Join(Prefix, TmpDir) } -func (s *service) checkAndExecute(version uint, user string, lang string, script string, fail e.Status) e.Status { - if !s.ProblemExists(version) { - s.log.Info("problem not exists", zap.Uint("version", version)) - return e.RunnerProblemNotExist - } - - if !s.userExists(user, fmt.Sprintf("%s.%s", user, lang)) { - s.log.Info("user program not exists", zap.String("user", user), zap.String("lang", lang)) - return e.RunnerUserNotExist - } - - err := s.execute(script, fmt.Sprintf("%d", version), user, lang) - - if err != nil { - s.log.Info("execute failed", - zap.Error(err), - zap.Uint("version", version), - zap.String("user", user), - zap.String("lang", lang)) - return fail - } - - return e.Success -} - func (s *service) check(version uint, user string, lang string) e.Status { if !s.ProblemExists(version) { s.log.Info("problem not exists", zap.Uint("version", version)) @@ -78,3 +53,20 @@ func (s *service) userExists(user string, name string) bool { userPath := filepath.Join(UserDir, user, name) return file.Exist(userPath) } + +func (s *service) getLangInfo(config *Config, lang string) (configLanguage, bool) { + for _, l := range config.Languages { + if l.Lang == lang { + return l, true + } + } + return configLanguage{}, false +} + +func (s *service) getLangScript(l *configLanguage, lang string) string { + if l.Type == "default" { + return "/woj/framework/template/default/" + lang + ".Makefile" + } else { + return "/woj/problem/judge/" + l.Script + } +} diff --git a/internal/service/runner/compile.go b/internal/service/runner/compile.go index e787283..fb75a71 100644 --- a/internal/service/runner/compile.go +++ b/internal/service/runner/compile.go @@ -12,30 +12,6 @@ import ( "time" ) -func (s *service) getProblemScript(version uint, lang string) (string, e.Status) { - config, err := s.ParseConfig(version, true) - if err != nil { - return "", e.RunnerProblemParseFailed - } - - var script string - for _, l := range config.Languages { - if l.Lang == lang { - if l.Type == "default" { - script = "/woj/framework/template/default/" + lang + ".Makefile" - } else { - script = "/woj/problem/judge/" + l.Script - } - break - } - } - - if script == "" { - return "", e.RunnerProblemParseFailed - } - return script, e.Success -} - func (s *service) Compile(version uint, user string, lang string) (JudgeStatus, e.Status) { // 1. ensure problem/user exists status := s.check(version, user, lang) @@ -43,15 +19,17 @@ func (s *service) Compile(version uint, user string, lang string) (JudgeStatus, return JudgeStatus{Message: "check failed"}, status } + config, err := s.ParseConfig(version, true) + if err != nil { + return JudgeStatus{Message: "parse config failed"}, e.RunnerProblemParseFailed + } + // 2. prepare judge environment workDir := filepath.Join(UserDir, user) judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", version), "judge") sourceFile := filepath.Join(workDir, fmt.Sprintf("%s.%s", user, lang)) - boxSourceFile := filepath.Join("/woj/problem/user", fmt.Sprintf("%s.%s", user, lang)) - targetFile := filepath.Join(workDir, fmt.Sprintf("%s.out", user)) - boxTargetFile := filepath.Join("/woj/problem/user", fmt.Sprintf("%s.out", user)) logFile := filepath.Join(workDir, fmt.Sprintf("%s.compile.log", user)) log, err := os.Create(logFile) @@ -64,25 +42,23 @@ func (s *service) Compile(version uint, user string, lang string) (JudgeStatus, // 3. compile err = utils.NewMust(). - Do(func() error { - _ = os.Remove(targetFile) - return nil - }). + DoAny(func() error { return os.Remove(targetFile) }). Do(func() error { return file.TouchErr(targetFile) }). Do(func() error { - script, err := s.getProblemScript(version, lang) - if err != e.Success { - return err.AsError() + l, ok := s.getLangInfo(&config, lang) + script := s.getLangScript(&l, lang) + if !ok { + return e.RunnerProblemParseFailed.AsError() } args := []string{ - "-v", judgeDir + ":/woj/problem/judge:ro", - "-v", sourceFile + ":" + boxSourceFile + ":ro", - "-v", targetFile + ":" + boxTargetFile, + "-v", fmt.Sprintf("%s:/woj/problem/judge:ro", judgeDir), + "-v", fmt.Sprintf("%s:/woj/user/%s.%s:ro", sourceFile, user, lang), + "-v", fmt.Sprintf("%s:/woj/user/%s.out", targetFile, user), "-e", fmt.Sprintf("USER_PROG=%s", user), "-e", fmt.Sprintf("LANG=%s", lang), "git.0x7f.app/woj/ubuntu-full:latest", - "sh", "-c", fmt.Sprintf("cd /woj/problem/user && make -f %s compile", script), + "sh", "-c", fmt.Sprintf("cd /woj/user && make -f %s compile", script), } runArgs := &podmanArgs{ diff --git a/internal/service/runner/config.go b/internal/service/runner/config.go index 844ffca..51fc1fa 100644 --- a/internal/service/runner/config.go +++ b/internal/service/runner/config.go @@ -8,19 +8,21 @@ import ( "path/filepath" ) +type configLanguage struct { + Lang string `json:"Lang"` + Type string `json:"Type,omitempty"` + Script string `json:"Script,omitempty"` + Cmp string `json:"Cmp,omitempty"` +} + type Config struct { Runtime struct { TimeLimit int `json:"TimeLimit"` MemoryLimit int `json:"MemoryLimit"` NProcLimit int `json:"NProcLimit"` } `json:"Runtime"` - Languages []struct { - Lang string `json:"Lang"` - Type string `json:"Type,omitempty"` - Script string `json:"Script,omitempty"` - Cmp string `json:"Cmp,omitempty"` - } `json:"Languages"` - Tasks []struct { + Languages []configLanguage `json:"Languages"` + Tasks []struct { Id int `json:"Id"` Points int32 `json:"Points"` } `json:"Tasks"` diff --git a/internal/service/runner/new_problem.go b/internal/service/runner/new_problem.go index c93a43b..9a5a0ba 100644 --- a/internal/service/runner/new_problem.go +++ b/internal/service/runner/new_problem.go @@ -53,8 +53,8 @@ func (s *service) prebuild(version uint, force bool) e.Status { judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", version), "judge") args := []string{ - "-v", dataDir + ":/woj/problem/data", - "-v", judgeDir + ":/woj/problem/judge", + "-v", fmt.Sprintf("%s:/woj/problem/data", dataDir), + "-v", fmt.Sprintf("%s:/woj/problem/judge", judgeDir), "-e", "PREFIX=/woj/problem", "git.0x7f.app/woj/ubuntu-full:latest", "sh", "-c", "cd /woj/problem/judge && make -f prebuild.Makefile prebuild && touch .mark.prebuild", diff --git a/internal/service/runner/runAndJudge.go b/internal/service/runner/runAndJudge.go deleted file mode 100644 index f0cf9ba..0000000 --- a/internal/service/runner/runAndJudge.go +++ /dev/null @@ -1,22 +0,0 @@ -package runner - -import "git.0x7f.app/WOJ/woj-server/internal/e" - -func (s *service) RunAndJudge(version uint, user string, lang string, config *Config) (JudgeStatus, int32, e.Status) { - // run user program - status := s.checkAndExecute(version, user, lang, "problem_run.sh", e.RunnerRunFailed) - if status != e.Success { - return JudgeStatus{Message: "run failed"}, 0, status - } - - // run judger - status = s.checkAndExecute(version, user, lang, "problem_judge.sh", e.RunnerJudgeFailed) - if status != e.Success { - return JudgeStatus{Message: "judge failed"}, 0, status - } - - // check result - result, pts := s.checkResults(user, config) - - return result, pts, e.Success -} diff --git a/internal/service/runner/run_judge.go b/internal/service/runner/run_judge.go new file mode 100644 index 0000000..01c2659 --- /dev/null +++ b/internal/service/runner/run_judge.go @@ -0,0 +1,183 @@ +package runner + +import ( + "fmt" + "git.0x7f.app/WOJ/woj-server/internal/e" + "git.0x7f.app/WOJ/woj-server/pkg/file" + "git.0x7f.app/WOJ/woj-server/pkg/utils" + "go.uber.org/zap" + "os" + "path/filepath" + "time" +) + +func (s *service) problemRun(version uint, user string, lang string, config *Config) { + workDir := filepath.Join(UserDir, user) + dataDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", version), "data", "input") + + // woj-sandbox killer will add 2 more seconds, here we add 2 more seconds + timeout := time.Duration((config.Runtime.TimeLimit+1000)/1000+2+2) * time.Second + + ids := make([]int, 0) + for _, task := range config.Tasks { + f := func(id int) func() { + return func() { + testCase := filepath.Join(dataDir, fmt.Sprintf("%d.input", id)) + targetFile := filepath.Join(workDir, fmt.Sprintf("%s.out", user)) + ansFile := filepath.Join(workDir, fmt.Sprintf("%d.out.usr", id)) + ifoFile := filepath.Join(workDir, fmt.Sprintf("%d.info", id)) + + err := utils.NewMust(). + DoAny(func() error { return os.Remove(ansFile) }). + DoAny(func() error { return os.Remove(ifoFile) }). + Do(func() error { return file.TouchErr(ansFile) }). + Do(func() error { return file.TouchErr(ifoFile) }). + Do(func() error { + args := []string{ + "--cpus", "1", + "--network", "none", + "-v", fmt.Sprintf("%s:/woj/problem/data/input/%d.input:ro", testCase, id), + "-v", fmt.Sprintf("%s:/woj/user/%s.out:ro", targetFile, user), + "-v", fmt.Sprintf("%s:/woj/user/%d.out.usr", ansFile, id), + "-v", fmt.Sprintf("%s:/woj/user/%d.info", ifoFile, id), + "git.0x7f.app/woj/ubuntu-run:latest", + "sh", "-c", + fmt.Sprintf("cd /woj/user && /woj/framework/scripts/woj_launcher "+ + "--memory_limit=%d "+ + "--nproc_limit=%d "+ + "--time_limit=%d "+ + "--sandbox_template=%s "+ + "--sandbox_action=ret "+ + "--uid=1000 "+ + "--gid=1000 "+ + "--file_input=/woj/problem/data/input/%d.input "+ + "--file_output=/woj/user/%d.out.usr "+ + "--file_info=/woj/user/%d.info "+ + "-program=/woj/user/%s.out", + config.Runtime.MemoryLimit, + config.Runtime.NProcLimit, + config.Runtime.TimeLimit, + lang, + id, id, id, + user, + ), + } + + runArgs := &podmanArgs{ + executeArgs: executeArgs{ + args: args, + timeout: timeout, + }, + } + + return s.podmanRun(runArgs) + }). + Done() + + if err != nil { + s.log.Info("run failed", + zap.Error(err), + zap.Uint("version", version), + zap.String("user", user), + zap.String("lang", lang), + ) + } + } + }(task.Id) + + id := s.pool.AddTask(f) + ids = append(ids, id) + } + + for _, id := range ids { + s.pool.WaitForTask(id) + } +} + +func (s *service) problemJudge(version uint, user string, lang string, config *Config) { + workDir := filepath.Join(UserDir, user) + dataDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", version), "data") + judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", version), "judge") + + ids := make([]int, 0) + for _, task := range config.Tasks { + f := func(id int) func() { + return func() { + ansFile := filepath.Join(workDir, fmt.Sprintf("%d.out.usr", id)) + jdgFile := filepath.Join(workDir, fmt.Sprintf("%d.judge", id)) + + c, ok := s.getLangInfo(config, lang) + if !ok { + return + } + + err := utils.NewMust(). + DoAny(func() error { return os.Remove(jdgFile) }). + Do(func() error { return file.TouchErr(jdgFile) }). + Do(func() error { + args := []string{ + "-v", fmt.Sprintf("%s:/woj/problem/judge:ro", judgeDir), + "-v", fmt.Sprintf("%s:/woj/problem/data:ro", dataDir), + "-v", fmt.Sprintf("%s:/woj/user/%d.out.usr", ansFile, id), + "-v", fmt.Sprintf("%s:/woj/user/%d.judge", jdgFile, id), + "-e", fmt.Sprintf("TEST_NUM=%d", id), + "-e", fmt.Sprintf("CMP=%s", c.Cmp), + "git.0x7f.app/woj/ubuntu-full:latest", + "sh", "-c", + fmt.Sprintf("cd /woj/user && make -f %s judge", s.getLangScript(&c, lang)), + } + + runArgs := &podmanArgs{ + executeArgs: executeArgs{ + args: args, + }, + } + + return s.podmanRun(runArgs) + }). + Done() + + if err != nil { + s.log.Info("judge failed", + zap.Error(err), + zap.Uint("version", version), + zap.String("user", user), + zap.String("lang", lang), + ) + } + } + }(task.Id) + + id := s.pool.AddTask(f) + ids = append(ids, id) + } + + for _, id := range ids { + s.pool.WaitForTask(id) + } +} + +func (s *service) RunAndJudge(version uint, user string, lang string) (JudgeStatus, int32, e.Status) { + // 1. ensure problem/user exists + status := s.check(version, user, lang) + if status != e.Success { + return JudgeStatus{Message: "check failed"}, 0, status + } + + // 2. config + config, err := s.ParseConfig(version, false) + if err != nil { + return JudgeStatus{Message: "parse config failed"}, 0, e.RunnerProblemParseFailed + } + + // 3. run user program + s.problemRun(version, user, lang, &config) + + // 4. run judger + s.problemJudge(version, user, lang, &config) + + // 5. check result + result, pts := s.checkResults(user, &config) + + return result, pts, e.Success +} diff --git a/internal/service/runner/service.go b/internal/service/runner/service.go index 112420d..6c3c97b 100644 --- a/internal/service/runner/service.go +++ b/internal/service/runner/service.go @@ -22,7 +22,7 @@ type Service interface { // Compile compile user submission Compile(version uint, user string, lang string) (JudgeStatus, e.Status) // RunAndJudge execute user program - RunAndJudge(version uint, user string, lang string, config *Config) (JudgeStatus, int32, e.Status) + RunAndJudge(version uint, user string, lang string) (JudgeStatus, int32, e.Status) // ParseConfig parse config file ParseConfig(version uint, skipCheck bool) (Config, error) @@ -30,16 +30,20 @@ type Service interface { ProblemExists(version uint) bool HealthCheck() error + Shutdown() error } func NewService(i *do.Injector) (Service, error) { concurrency := utils.If(runtime.NumCPU() > 1, runtime.NumCPU()-1, 1) - return &service{ + srv := &service{ log: do.MustInvoke[log.Service](i).GetLogger("runner"), pool: pool.NewTaskPool(concurrency, concurrency), verbose: do.MustInvoke[config.Service](i).GetConfig().Development, - }, nil + } + srv.pool.Start() + + return srv, nil } type service struct { @@ -51,3 +55,8 @@ type service struct { func (s *service) HealthCheck() error { return nil } + +func (s *service) Shutdown() error { + s.pool.Stop() + return nil +} diff --git a/pkg/utils/must.go b/pkg/utils/must.go index 75b24e3..152b636 100644 --- a/pkg/utils/must.go +++ b/pkg/utils/must.go @@ -15,6 +15,11 @@ func (c *MustChain) Do(callback func() error) *MustChain { return c } +func (c *MustChain) DoAny(callback func() error) *MustChain { + _ = callback() + return c +} + func (c *MustChain) Done() error { return c.err } diff --git a/resource/runner/scripts/docker_run.sh b/resource/runner/scripts/docker_run.sh deleted file mode 100755 index 4ebc9b6..0000000 --- a/resource/runner/scripts/docker_run.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -SCRIPT_PATH=$(cd "$(dirname "$0")" && pwd) -. "$SCRIPT_PATH/common.sh" - -function docker_run() { - local timeout=${TIMEOUT:-10} - local memory=${MEMORY:-"256m"} - local log_file=${LOG_FILE:-"/dev/stderr"} - local log_limit=${LOG_LIMIT:-4K} - log_info "$DOCKER run with timeout $timeout" - CONTAINER_NAME=$(uuidgen) - ( - sleep "$timeout" - $DOCKER kill "$CONTAINER_NAME" - ) & - $DOCKER run --rm --name "$CONTAINER_NAME" --memory "$memory" "$@" 2>&1 | head -c "$log_limit" >"$log_file" - pkill -P $$ - $DOCKER kill "$CONTAINER_NAME" >/dev/null 2>&1 - return 0 -} diff --git a/resource/runner/scripts/problem.sh b/resource/runner/scripts/problem.sh deleted file mode 100755 index 835499c..0000000 --- a/resource/runner/scripts/problem.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash - -SCRIPT_PATH=$(cd "$(dirname "$0")" && pwd) -. "$SCRIPT_PATH/common.sh" - -# get_problem_info -# extract language info and limits -# $1: workspace -# $2: problem name -# $3: language -# exports: Info_Script, Info_Cmp, Info_Num, Info_Limit_Time, Info_Limit_Memory, Info_Limit_NProc -function get_problem_info() { - local err - - if [ ! -f "$1/problem/$2/config.json" ]; then - log_error "problem $2 not found" - return 1 - fi - - parse_language_info "$1" "$2" "$3" - err=$? - if [ "$err" -ne 0 ]; then - return "$err" - fi - - parse_limits "$1" "$2" - err=$? - if [ "$err" -ne 0 ]; then - return "$err" - fi -} - -function parse_language_info() { - export Info_Script - export Info_Cmp - - local lang_config - local lang_type - local lang_script - - lang_config=$(jq ".Languages[] | select(.Lang == \"$3\")" "$1/problem/$2/config.json") - if [ -z "$lang_config" ]; then - log_error "language $3 is not supported" - return 1 - fi - - Info_Cmp=$(echo "$lang_config" | jq -r ".Cmp") - - lang_type=$(echo "$lang_config" | jq -r ".Type") - lang_script=$(echo "$lang_config" | jq -r ".Script") - - if [ "$lang_type" == "custom" ]; then - Info_Script="/woj/problem/judge/$lang_script" - elif [ "$lang_type" == "default" ]; then - Info_Script="/woj/framework/template/default/$3.Makefile" - else - log_warn "Config file might be corrupted!" - log_error "Unknown language type: $lang_type" - return 1 - fi -} - -function parse_limits() { - export Info_Limit_Time - export Info_Limit_Memory - export Info_Limit_NProc - export Info_Num - - local cfg - cfg="$1/problem/$2/config.json" - - Info_Limit_Time=$(jq ".Runtime.TimeLimit" "$cfg") - Info_Limit_Memory=$(jq ".Runtime.MemoryLimit" "$cfg") - Info_Limit_NProc=$(jq ".Runtime.NProcLimit" "$cfg") - Info_Num=$(jq ".Tasks | length" "$1/problem/$2/config.json") -} diff --git a/resource/runner/scripts/problem_judge.sh b/resource/runner/scripts/problem_judge.sh deleted file mode 100755 index c94f4bb..0000000 --- a/resource/runner/scripts/problem_judge.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash - -WORKSPACE=$(cd "$(dirname "$0")"/.. && pwd) -. "$WORKSPACE"/scripts/common.sh -. "$WORKSPACE"/scripts/docker_run.sh -. "$WORKSPACE"/scripts/problem.sh - -if [ "$1" == "" ] || [ ! -d "$WORKSPACE/problem/$1" ] || [ "$2" == "" ] || [ ! -d "$WORKSPACE/user/$2" ] || [ -z "$3" ]; then - log_warn "Usage: $0 " - exit 1 -fi - -get_problem_info "$WORKSPACE" "$1" "$3" - -export TIMEOUT=${4:-60} -for test_num in $(seq "$Info_Num"); do - std_file="$WORKSPACE/problem/$1/data/output/$test_num.output" - ans_file="$WORKSPACE/user/$2/$test_num.out.usr" - jdg_file="$WORKSPACE/user/$2/$test_num.judge" - - if [ ! -f "$std_file" ] || [ ! -f "$ans_file" ]; then - log_error "Missing test case $test_num" - exit 1 - fi - - log_info "Judging test case $test_num" - - touch "$jdg_file" - - docker_run \ - -v "$WORKSPACE"/problem/"$1"/judge:/woj/problem/judge:ro \ - -v "$WORKSPACE"/problem/"$1"/data:/woj/problem/data:ro \ - -v "$ans_file":/woj/problem/user/"$test_num".out.usr \ - -v "$jdg_file":/woj/problem/user/"$test_num".judge \ - -e TEST_NUM="$test_num" \ - -e CMP="$Info_Cmp" \ - git.0x7f.app/woj/ubuntu-full \ - sh -c \ - "cd /woj/problem/user && make -f $Info_Script judge" -done diff --git a/resource/runner/scripts/problem_run.sh b/resource/runner/scripts/problem_run.sh deleted file mode 100755 index dc4165a..0000000 --- a/resource/runner/scripts/problem_run.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env bash - -WORKSPACE=$(cd "$(dirname "$0")"/.. && pwd) -. "$WORKSPACE"/scripts/common.sh -. "$WORKSPACE"/scripts/docker_run.sh -. "$WORKSPACE"/scripts/problem.sh - -if [ "$1" == "" ] || [ ! -d "$WORKSPACE/problem/$1" ] || [ "$2" == "" ] || [ ! -d "$WORKSPACE/user/$2" ] || [ -z "$3" ]; then - log_warn "Usage: $0 " - exit 1 -fi - -if [ ! -f "$WORKSPACE/problem/$1/.mark.prebuild" ]; then - log_warn "Problem $1 has not been prebuilt" - log_warn "Please run 'problem_prebuild.sh $1' first" - exit 1 -fi - -if [ ! -f "$WORKSPACE/user/$2/$2.out" ]; then - log_warn "User $2 has not been compiled" - log_warn "Please run 'problem_compile.sh ...' first" - exit 1 -fi - -parse_limits "$WORKSPACE" "$1" - -log_info "Running problem $1 for user $2" -log_info "TimeLimit: $Info_Limit_Time" -log_info "MemoryLimit: $Info_Limit_Memory" -log_info "NProcLimit: $Info_Limit_NProc" - -# launcher will add 2 more seconds -# here add 3 more seconds -TIMEOUT=$(((LIMIT_TIME + 1000) / 1000 + 4)) -log_info "Timeout: $TIMEOUT" - -for test_num in $(seq "$Info_Num"); do - test_case="$WORKSPACE/problem/$1/data/input/$test_num.input" - exe_file="$WORKSPACE/user/$2/$2.out" - ans_file="$WORKSPACE/user/$2/$test_num.out.usr" - ifo_file="$WORKSPACE/user/$2/$test_num.info" - - if [ ! -f "$test_case" ]; then - log_error "Test case $test_num does not exist" - exit 1 - fi - - log_info "Running test case $test_num" - rm -f "$ans_file" && touch "$ans_file" - rm -f "$ifo_file" && touch "$ifo_file" - docker_run \ - --cpus 1 \ - --network none \ - -v "$test_case":/woj/problem/data/input/"$test_num".input:ro \ - -v "$exe_file":/woj/user/"$2".out:ro \ - -v "$ans_file":/woj/user/"$test_num".out.usr \ - -v "$ifo_file":/woj/user/"$test_num".info \ - git.0x7f.app/woj/ubuntu-run \ - sh -c \ - "cd /woj/user && /woj/framework/scripts/woj_launcher \ - --memory_limit=$Info_Limit_Memory \ - --nproc_limit=$Info_Limit_NProc \ - --time_limit=$Info_Limit_Time \ - --sandbox_template=$3 \ - --sandbox_action=ret \ - --uid=1000 \ - --gid=1000 \ - --file_input=/woj/problem/data/input/$test_num.input \ - --file_output=/woj/user/$test_num.out.usr \ - --file_info=/woj/user/$test_num.info \ - --program=/woj/user/$2.out" -done