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] 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] 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 }