feat: #6 [7] Rewrite run_judge
This commit is contained in:
parent
8dee13af85
commit
bb105d1451
@ -56,14 +56,8 @@ func (h *handler) Judge(_ context.Context, t *asynq.Task) error {
|
|||||||
return e.Success, 0, compileResult
|
return e.Success, 0, compileResult
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. config
|
// 4. run and judge
|
||||||
config, err := h.runnerService.ParseConfig(p.ProblemVersionID, true)
|
result, point, status := h.runnerService.RunAndJudge(p.ProblemVersionID, user, p.Submission.Language)
|
||||||
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)
|
|
||||||
return utils.If(status != e.Success, e.InternalError, e.Success), point, result
|
return utils.If(status != e.Success, e.InternalError, e.Success), point, result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -30,31 +30,6 @@ func init() {
|
|||||||
TmpDir = path.Join(Prefix, TmpDir)
|
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 {
|
func (s *service) check(version uint, user string, lang string) e.Status {
|
||||||
if !s.ProblemExists(version) {
|
if !s.ProblemExists(version) {
|
||||||
s.log.Info("problem not exists", zap.Uint("version", 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)
|
userPath := filepath.Join(UserDir, user, name)
|
||||||
return file.Exist(userPath)
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,30 +12,6 @@ import (
|
|||||||
"time"
|
"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) {
|
func (s *service) Compile(version uint, user string, lang string) (JudgeStatus, e.Status) {
|
||||||
// 1. ensure problem/user exists
|
// 1. ensure problem/user exists
|
||||||
status := s.check(version, user, lang)
|
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
|
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
|
// 2. prepare judge environment
|
||||||
workDir := filepath.Join(UserDir, user)
|
workDir := filepath.Join(UserDir, user)
|
||||||
judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", version), "judge")
|
judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", version), "judge")
|
||||||
|
|
||||||
sourceFile := filepath.Join(workDir, fmt.Sprintf("%s.%s", user, lang))
|
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))
|
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))
|
logFile := filepath.Join(workDir, fmt.Sprintf("%s.compile.log", user))
|
||||||
log, err := os.Create(logFile)
|
log, err := os.Create(logFile)
|
||||||
@ -64,25 +42,23 @@ func (s *service) Compile(version uint, user string, lang string) (JudgeStatus,
|
|||||||
|
|
||||||
// 3. compile
|
// 3. compile
|
||||||
err = utils.NewMust().
|
err = utils.NewMust().
|
||||||
Do(func() error {
|
DoAny(func() error { return os.Remove(targetFile) }).
|
||||||
_ = os.Remove(targetFile)
|
|
||||||
return nil
|
|
||||||
}).
|
|
||||||
Do(func() error { return file.TouchErr(targetFile) }).
|
Do(func() error { return file.TouchErr(targetFile) }).
|
||||||
Do(func() error {
|
Do(func() error {
|
||||||
script, err := s.getProblemScript(version, lang)
|
l, ok := s.getLangInfo(&config, lang)
|
||||||
if err != e.Success {
|
script := s.getLangScript(&l, lang)
|
||||||
return err.AsError()
|
if !ok {
|
||||||
|
return e.RunnerProblemParseFailed.AsError()
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"-v", judgeDir + ":/woj/problem/judge:ro",
|
"-v", fmt.Sprintf("%s:/woj/problem/judge:ro", judgeDir),
|
||||||
"-v", sourceFile + ":" + boxSourceFile + ":ro",
|
"-v", fmt.Sprintf("%s:/woj/user/%s.%s:ro", sourceFile, user, lang),
|
||||||
"-v", targetFile + ":" + boxTargetFile,
|
"-v", fmt.Sprintf("%s:/woj/user/%s.out", targetFile, user),
|
||||||
"-e", fmt.Sprintf("USER_PROG=%s", user),
|
"-e", fmt.Sprintf("USER_PROG=%s", user),
|
||||||
"-e", fmt.Sprintf("LANG=%s", lang),
|
"-e", fmt.Sprintf("LANG=%s", lang),
|
||||||
"git.0x7f.app/woj/ubuntu-full:latest",
|
"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{
|
runArgs := &podmanArgs{
|
||||||
|
@ -8,18 +8,20 @@ import (
|
|||||||
"path/filepath"
|
"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 {
|
type Config struct {
|
||||||
Runtime struct {
|
Runtime struct {
|
||||||
TimeLimit int `json:"TimeLimit"`
|
TimeLimit int `json:"TimeLimit"`
|
||||||
MemoryLimit int `json:"MemoryLimit"`
|
MemoryLimit int `json:"MemoryLimit"`
|
||||||
NProcLimit int `json:"NProcLimit"`
|
NProcLimit int `json:"NProcLimit"`
|
||||||
} `json:"Runtime"`
|
} `json:"Runtime"`
|
||||||
Languages []struct {
|
Languages []configLanguage `json:"Languages"`
|
||||||
Lang string `json:"Lang"`
|
|
||||||
Type string `json:"Type,omitempty"`
|
|
||||||
Script string `json:"Script,omitempty"`
|
|
||||||
Cmp string `json:"Cmp,omitempty"`
|
|
||||||
} `json:"Languages"`
|
|
||||||
Tasks []struct {
|
Tasks []struct {
|
||||||
Id int `json:"Id"`
|
Id int `json:"Id"`
|
||||||
Points int32 `json:"Points"`
|
Points int32 `json:"Points"`
|
||||||
|
@ -53,8 +53,8 @@ func (s *service) prebuild(version uint, force bool) e.Status {
|
|||||||
judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", version), "judge")
|
judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", version), "judge")
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"-v", dataDir + ":/woj/problem/data",
|
"-v", fmt.Sprintf("%s:/woj/problem/data", dataDir),
|
||||||
"-v", judgeDir + ":/woj/problem/judge",
|
"-v", fmt.Sprintf("%s:/woj/problem/judge", judgeDir),
|
||||||
"-e", "PREFIX=/woj/problem",
|
"-e", "PREFIX=/woj/problem",
|
||||||
"git.0x7f.app/woj/ubuntu-full:latest",
|
"git.0x7f.app/woj/ubuntu-full:latest",
|
||||||
"sh", "-c", "cd /woj/problem/judge && make -f prebuild.Makefile prebuild && touch .mark.prebuild",
|
"sh", "-c", "cd /woj/problem/judge && make -f prebuild.Makefile prebuild && touch .mark.prebuild",
|
||||||
|
@ -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
|
|
||||||
}
|
|
183
internal/service/runner/run_judge.go
Normal file
183
internal/service/runner/run_judge.go
Normal file
@ -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
|
||||||
|
}
|
@ -22,7 +22,7 @@ type Service interface {
|
|||||||
// Compile compile user submission
|
// Compile compile user submission
|
||||||
Compile(version uint, user string, lang string) (JudgeStatus, e.Status)
|
Compile(version uint, user string, lang string) (JudgeStatus, e.Status)
|
||||||
// RunAndJudge execute user program
|
// 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 parse config file
|
||||||
ParseConfig(version uint, skipCheck bool) (Config, error)
|
ParseConfig(version uint, skipCheck bool) (Config, error)
|
||||||
@ -30,16 +30,20 @@ type Service interface {
|
|||||||
ProblemExists(version uint) bool
|
ProblemExists(version uint) bool
|
||||||
|
|
||||||
HealthCheck() error
|
HealthCheck() error
|
||||||
|
Shutdown() error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(i *do.Injector) (Service, error) {
|
func NewService(i *do.Injector) (Service, error) {
|
||||||
concurrency := utils.If(runtime.NumCPU() > 1, runtime.NumCPU()-1, 1)
|
concurrency := utils.If(runtime.NumCPU() > 1, runtime.NumCPU()-1, 1)
|
||||||
|
|
||||||
return &service{
|
srv := &service{
|
||||||
log: do.MustInvoke[log.Service](i).GetLogger("runner"),
|
log: do.MustInvoke[log.Service](i).GetLogger("runner"),
|
||||||
pool: pool.NewTaskPool(concurrency, concurrency),
|
pool: pool.NewTaskPool(concurrency, concurrency),
|
||||||
verbose: do.MustInvoke[config.Service](i).GetConfig().Development,
|
verbose: do.MustInvoke[config.Service](i).GetConfig().Development,
|
||||||
}, nil
|
}
|
||||||
|
srv.pool.Start()
|
||||||
|
|
||||||
|
return srv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
@ -51,3 +55,8 @@ type service struct {
|
|||||||
func (s *service) HealthCheck() error {
|
func (s *service) HealthCheck() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) Shutdown() error {
|
||||||
|
s.pool.Stop()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -15,6 +15,11 @@ func (c *MustChain) Do(callback func() error) *MustChain {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *MustChain) DoAny(callback func() error) *MustChain {
|
||||||
|
_ = callback()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
func (c *MustChain) Done() error {
|
func (c *MustChain) Done() error {
|
||||||
return c.err
|
return c.err
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -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")
|
|
||||||
}
|
|
@ -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 <problem> <user_dir> <language> <timeout>"
|
|
||||||
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
|
|
@ -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 <problem> <user_dir> <language>"
|
|
||||||
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
|
|
Loading…
Reference in New Issue
Block a user