mirror of
https://github.com/criyle/go-judge.git
synced 2025-11-04 14:50:02 +08:00
fixed typo
This commit is contained in:
parent
ecaadcc36e
commit
ce8a78454f
@ -13,5 +13,5 @@ type Task interface {
|
||||
// it should sent received work through go channel (have background goroutine(s))
|
||||
type Client interface {
|
||||
// C should return channel to receive works
|
||||
C() <-chan types.JudgeTask
|
||||
C() <-chan Task
|
||||
}
|
||||
|
||||
5
go.mod
5
go.mod
@ -2,4 +2,7 @@ module github.com/criyle/go-judge
|
||||
|
||||
go 1.12
|
||||
|
||||
require github.com/criyle/go-sandbox v0.0.0-20190902024918-1df3228aec4d
|
||||
require (
|
||||
github.com/criyle/go-sandbox v0.0.0-20190906040622-be668a5e7548
|
||||
golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03 // indirect
|
||||
)
|
||||
|
||||
9
go.sum
9
go.sum
@ -1,5 +1,6 @@
|
||||
github.com/criyle/go-sandbox v0.0.0-20190902024918-1df3228aec4d h1:JpdxvAKkHYskHPGuVm7hPnndOBuCygkOxc1p8V1rPeI=
|
||||
github.com/criyle/go-sandbox v0.0.0-20190902024918-1df3228aec4d/go.mod h1:RZ6jui4DXDFZHojTt5pNxr6FXUbCcW+TLITDSANJllk=
|
||||
github.com/criyle/go-sandbox v0.0.0-20190906040622-be668a5e7548 h1:5JzsTB6UUHCoZkgjBmU68WYhIdbs189xtkUHJs+qu4c=
|
||||
github.com/criyle/go-sandbox v0.0.0-20190906040622-be668a5e7548/go.mod h1:gkn5TWck4A8kKs8xX0q1+yLA0WnV2e7ECm/OmYigDCI=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
golang.org/x/sys v0.0.0-20190830142957-1e83adbbebd0 h1:7z820YPX9pxWR59qM7BE5+fglp4D/mKqAwCvGt11b+8=
|
||||
golang.org/x/sys v0.0.0-20190830142957-1e83adbbebd0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904005037-43c01164e931/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03 h1:b3JiLYVaG9kHjTcOQIoUh978YMCO7oVTQQBLudU47zY=
|
||||
golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
||||
@ -1 +1,14 @@
|
||||
package judger
|
||||
|
||||
import (
|
||||
"github.com/criyle/go-judge/client"
|
||||
"github.com/criyle/go-judge/problem"
|
||||
"github.com/criyle/go-judge/taskqueue"
|
||||
)
|
||||
|
||||
// Judger receives task from client and translate to task for runner
|
||||
type Judger struct {
|
||||
client.Client
|
||||
taskqueue.Queue
|
||||
problem.Builder
|
||||
}
|
||||
|
||||
138
judger/loop.go
Normal file
138
judger/loop.go
Normal file
@ -0,0 +1,138 @@
|
||||
package judger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/criyle/go-judge/client"
|
||||
"github.com/criyle/go-judge/file"
|
||||
"github.com/criyle/go-judge/types"
|
||||
)
|
||||
|
||||
// Loop fetch judge task from client and report results
|
||||
// in a infinite loop
|
||||
func (j *Judger) Loop(done <-chan struct{}) {
|
||||
c := j.Client.C()
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case t := <-c:
|
||||
rt := j.run(done, t)
|
||||
t.Finish(rt)
|
||||
select {
|
||||
case <-done:
|
||||
break loop
|
||||
default:
|
||||
}
|
||||
case <-done:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (j *Judger) run(done <-chan struct{}, t client.Task) types.JudgeResult {
|
||||
var result types.JudgeResult
|
||||
errResult := func(err error) types.JudgeResult {
|
||||
result.Error = err.Error()
|
||||
return result
|
||||
}
|
||||
|
||||
p := t.Param()
|
||||
pconf, err := j.Build(p.TestData)
|
||||
if err != nil {
|
||||
return errResult(err)
|
||||
}
|
||||
|
||||
// compile
|
||||
compileRet := make(chan types.RunTaskResult)
|
||||
err = j.Enqueue(types.RunTask{
|
||||
Type: "compile",
|
||||
Language: p.Language,
|
||||
Code: p.Code,
|
||||
ExtraFiles: pconf.ExtraFiles,
|
||||
}, compileRet)
|
||||
if err != nil {
|
||||
return errResult(err)
|
||||
}
|
||||
compileTaskResult := <-compileRet
|
||||
if compileTaskResult.Error != "" {
|
||||
return errResult(err)
|
||||
}
|
||||
execFiles := compileTaskResult.ExecFiles
|
||||
|
||||
// run
|
||||
subTaskResult := make(chan types.JudgeSubTaskResult, len(pconf.Subtasks))
|
||||
pj := problemJudger{
|
||||
Judger: j,
|
||||
ProblemConfig: &pconf,
|
||||
Task: t,
|
||||
JudgeTask: p,
|
||||
total: count(&pconf),
|
||||
}
|
||||
for _, s := range pconf.Subtasks {
|
||||
s := &s
|
||||
go func() {
|
||||
subTaskResult <- pj.runSubtask(done, execFiles, s)
|
||||
}()
|
||||
}
|
||||
for range pconf.Subtasks {
|
||||
result.SubTasks = append(result.SubTasks, <-subTaskResult)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type problemJudger struct {
|
||||
*Judger
|
||||
*types.ProblemConfig
|
||||
*types.JudgeTask
|
||||
client.Task
|
||||
count int32
|
||||
total int32
|
||||
}
|
||||
|
||||
func (pj *problemJudger) runSubtask(done <-chan struct{}, exec []file.File, s *types.SubTask) types.JudgeSubTaskResult {
|
||||
var result types.JudgeSubTaskResult
|
||||
caseResult := make(chan types.RunTaskResult, len(s.Cases))
|
||||
for _, c := range s.Cases {
|
||||
pj.Enqueue(types.RunTask{
|
||||
Type: pj.ProblemConfig.Type,
|
||||
Language: pj.Language,
|
||||
TimeLimit: pj.TileLimit,
|
||||
MemoryLimit: pj.MemoryLimit,
|
||||
Executables: exec,
|
||||
InputFile: c.Input,
|
||||
AnswerFile: c.Answer,
|
||||
}, caseResult)
|
||||
}
|
||||
for range s.Cases {
|
||||
rt := <-caseResult
|
||||
result.Cases = append(result.Cases, types.JudgeCaseResult{
|
||||
Status: rt.Status,
|
||||
ScoreRate: rt.ScoringRate,
|
||||
Error: rt.Error,
|
||||
Time: rt.Time,
|
||||
Memory: rt.Memory,
|
||||
Input: rt.Input,
|
||||
Answer: rt.Answer,
|
||||
UserOutput: rt.UserOutput,
|
||||
UserError: rt.UserError,
|
||||
SpjOutput: rt.SpjOutput,
|
||||
})
|
||||
result.Score += rt.ScoringRate
|
||||
// report prograss
|
||||
atomic.AddInt32(&pj.count, 1)
|
||||
pj.Progress(types.JudgeProgress{
|
||||
Message: fmt.Sprintf("%d/%d", pj.count, pj.total),
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// count counts total number of cases
|
||||
func count(pconf *types.ProblemConfig) int32 {
|
||||
var count int32
|
||||
for _, s := range pconf.Subtasks {
|
||||
count += int32(len(s.Cases))
|
||||
}
|
||||
return count
|
||||
}
|
||||
@ -1,15 +1,26 @@
|
||||
package language
|
||||
|
||||
// Type defines compile / exec
|
||||
type Type int
|
||||
|
||||
// Defines the exec type
|
||||
const (
|
||||
TypeCompile Type = iota + 1
|
||||
TypeExec
|
||||
)
|
||||
|
||||
// Language defines the way to run program
|
||||
type Language interface {
|
||||
Get(string, string) ExecParam // Get execparam for specific language and type (compile / run)
|
||||
Get(string, Type) ExecParam // Get execparam for specific language and type (compile / run)
|
||||
}
|
||||
|
||||
// ExecParam defines specs to compile / run program
|
||||
type ExecParam struct {
|
||||
SourceFileName string
|
||||
Args []string
|
||||
CompiledFileNames []string
|
||||
|
||||
// Compile
|
||||
SourceFileName string // put code when compile
|
||||
CompiledFileNames []string // exec files
|
||||
|
||||
// limits
|
||||
TimeLimit uint64
|
||||
|
||||
1
pkg/diff/diff.go
Normal file
1
pkg/diff/diff.go
Normal file
@ -0,0 +1 @@
|
||||
package diff
|
||||
11
problem/interface.go
Normal file
11
problem/interface.go
Normal file
@ -0,0 +1,11 @@
|
||||
package problem
|
||||
|
||||
import (
|
||||
"github.com/criyle/go-judge/file"
|
||||
"github.com/criyle/go-judge/types"
|
||||
)
|
||||
|
||||
// Builder builds problem specs from file
|
||||
type Builder interface {
|
||||
Build([]file.File) (types.ProblemConfig, error)
|
||||
}
|
||||
@ -3,11 +3,11 @@ package runner
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/criyle/go-sandbox/deamon"
|
||||
"github.com/criyle/go-sandbox/daemon"
|
||||
)
|
||||
|
||||
type pool struct {
|
||||
queue chan *deamon.Master
|
||||
queue chan *daemon.Master
|
||||
count int32
|
||||
root string
|
||||
}
|
||||
@ -16,26 +16,26 @@ const maxPoolSize = 64
|
||||
|
||||
func newPool(root string) *pool {
|
||||
return &pool{
|
||||
queue: make(chan *deamon.Master, maxPoolSize),
|
||||
queue: make(chan *daemon.Master, maxPoolSize),
|
||||
root: root,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pool) Get() (*deamon.Master, error) {
|
||||
func (p *pool) Get() (*daemon.Master, error) {
|
||||
select {
|
||||
case m := <-p.queue:
|
||||
return m, nil
|
||||
default:
|
||||
}
|
||||
atomic.AddInt32(&p.count, 1)
|
||||
return deamon.New(p.root)
|
||||
return daemon.New(p.root)
|
||||
}
|
||||
|
||||
func (p *pool) Put(master *deamon.Master) {
|
||||
func (p *pool) Put(master *daemon.Master) {
|
||||
p.queue <- master
|
||||
}
|
||||
|
||||
func (p *pool) Destroy(master *deamon.Master) {
|
||||
func (p *pool) Destroy(master *daemon.Master) {
|
||||
master.Destroy()
|
||||
atomic.AddInt32(&p.count, -1)
|
||||
}
|
||||
|
||||
@ -1,13 +1,17 @@
|
||||
package runner
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/criyle/go-sandbox/deamon"
|
||||
"github.com/criyle/go-sandbox/daemon"
|
||||
"github.com/criyle/go-sandbox/pkg/cgroup"
|
||||
"github.com/criyle/go-sandbox/pkg/pipe"
|
||||
stypes "github.com/criyle/go-sandbox/types"
|
||||
|
||||
"github.com/criyle/go-judge/file"
|
||||
"github.com/criyle/go-judge/file/memfile"
|
||||
"github.com/criyle/go-judge/language"
|
||||
"github.com/criyle/go-judge/types"
|
||||
)
|
||||
|
||||
@ -19,7 +23,11 @@ const checkIntervalMS = 50
|
||||
var env = []string{"PATH=/usr/local/bin:/usr/bin:/bin"}
|
||||
|
||||
func (r *Runner) run(done <-chan struct{}, task *types.RunTask) *types.RunTaskResult {
|
||||
param := r.Language.Get(task.Language, task.Type)
|
||||
t := language.TypeExec
|
||||
if task.Type == "compile" {
|
||||
t = language.TypeCompile
|
||||
}
|
||||
param := r.Language.Get(task.Language, t)
|
||||
|
||||
// init input / output / error files
|
||||
inputFile, err := task.InputFile.Open()
|
||||
@ -47,10 +55,10 @@ func (r *Runner) run(done <-chan struct{}, task *types.RunTask) *types.RunTaskRe
|
||||
}
|
||||
defer cg.Destroy()
|
||||
|
||||
// get deamon runner
|
||||
// get daemon runner
|
||||
m, err := r.pool.Get()
|
||||
if err != nil {
|
||||
return errResult("failed to get deamon instance")
|
||||
return errResult("failed to get daemon instance")
|
||||
}
|
||||
defer r.pool.Put(m)
|
||||
|
||||
@ -64,7 +72,7 @@ func (r *Runner) run(done <-chan struct{}, task *types.RunTask) *types.RunTaskRe
|
||||
cg.SetPidsMax(param.ProcLimit)
|
||||
|
||||
// set running parameters
|
||||
execParam := deamon.ExecveParam{
|
||||
execParam := daemon.ExecveParam{
|
||||
Args: param.Args,
|
||||
Envv: env,
|
||||
Fds: []uintptr{inputFile.Fd(), outputPipe.W.Fd(), errorPipe.W.Fd()},
|
||||
@ -72,7 +80,7 @@ func (r *Runner) run(done <-chan struct{}, task *types.RunTask) *types.RunTaskRe
|
||||
}
|
||||
|
||||
// cancellable signal channel
|
||||
cancelC := newCancelableChannel()
|
||||
cancelC := newCancellableChannel()
|
||||
defer cancelC.cancel()
|
||||
|
||||
// start the process
|
||||
@ -169,6 +177,27 @@ loop:
|
||||
}
|
||||
|
||||
inputContent, _ := task.InputFile.Content()
|
||||
|
||||
// If compile read compiled files
|
||||
var exec []file.File
|
||||
if task.Type == "compile" {
|
||||
for _, fn := range param.CompiledFileNames {
|
||||
f, err := m.Open(fn)
|
||||
if err != nil {
|
||||
return errResult(err.Error())
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
c, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return errResult(err.Error())
|
||||
}
|
||||
exec = append(exec, memfile.New(fn, c))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: diff
|
||||
|
||||
return &types.RunTaskResult{
|
||||
Status: status,
|
||||
Time: cpuUsage / uint64(time.Millisecond),
|
||||
@ -176,6 +205,7 @@ loop:
|
||||
Input: inputContent,
|
||||
UserOutput: outputPipe.Buffer.Bytes(),
|
||||
UserError: errorPipe.Buffer.Bytes(),
|
||||
ExecFiles: exec,
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,18 +216,18 @@ func errResult(err string) *types.RunTaskResult {
|
||||
}
|
||||
}
|
||||
|
||||
type cancelableChannel struct {
|
||||
type cancellableChannel struct {
|
||||
Done chan struct{}
|
||||
canceled bool
|
||||
}
|
||||
|
||||
func newCancelableChannel() *cancelableChannel {
|
||||
return &cancelableChannel{
|
||||
func newCancellableChannel() *cancellableChannel {
|
||||
return &cancellableChannel{
|
||||
Done: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cancelableChannel) cancel() {
|
||||
func (c *cancellableChannel) cancel() {
|
||||
if !c.canceled {
|
||||
close(c.Done)
|
||||
c.canceled = true
|
||||
|
||||
50
taskqueue/channel/channel.go
Normal file
50
taskqueue/channel/channel.go
Normal file
@ -0,0 +1,50 @@
|
||||
package channel
|
||||
|
||||
import (
|
||||
"github.com/criyle/go-judge/taskqueue"
|
||||
"github.com/criyle/go-judge/types"
|
||||
)
|
||||
|
||||
const buffSize = 512
|
||||
|
||||
// Queue implements taskqueue by go channel
|
||||
type Queue struct {
|
||||
queue chan taskqueue.Task
|
||||
}
|
||||
|
||||
// New craetes new Queue with buffed go channel
|
||||
func New() *Queue {
|
||||
return &Queue{
|
||||
queue: make(chan taskqueue.Task, buffSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Enqueue puts task into run queue
|
||||
func (q *Queue) Enqueue(t types.RunTask, r chan<- types.RunTaskResult) error {
|
||||
q.queue <- Task{
|
||||
task: t,
|
||||
result: r,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// C returns the underlying channel
|
||||
func (q *Queue) C() <-chan taskqueue.Task {
|
||||
return q.queue
|
||||
}
|
||||
|
||||
// Task implements Task interface
|
||||
type Task struct {
|
||||
task types.RunTask
|
||||
result chan<- types.RunTaskResult
|
||||
}
|
||||
|
||||
// Task returns task parameters
|
||||
func (t Task) Task() *types.RunTask {
|
||||
return &t.task
|
||||
}
|
||||
|
||||
// Finish returns the run task result
|
||||
func (t Task) Finish(r *types.RunTaskResult) {
|
||||
t.result <- *r
|
||||
}
|
||||
@ -21,6 +21,7 @@ type JudgeProgress struct {
|
||||
// JudgeResult contains final result of current task
|
||||
type JudgeResult struct {
|
||||
SubTasks []JudgeSubTaskResult
|
||||
Error string
|
||||
}
|
||||
|
||||
// JudgeSubTaskResult contains result for single sub-task
|
||||
|
||||
@ -25,6 +25,6 @@ type SubTask struct {
|
||||
|
||||
// Case defines single judge case
|
||||
type Case struct {
|
||||
Input []file.File
|
||||
Answer []file.File
|
||||
Input file.File
|
||||
Answer file.File
|
||||
}
|
||||
|
||||
@ -36,6 +36,8 @@ type RunTaskResult struct {
|
||||
Status string
|
||||
// error
|
||||
Error string
|
||||
// compile result
|
||||
ExecFiles []file.File
|
||||
// details
|
||||
Time uint64 // ms
|
||||
Memory uint64 // kb
|
||||
|
||||
Loading…
Reference in New Issue
Block a user