fixed typo

This commit is contained in:
criyle 2019-09-07 18:03:19 -07:00
parent ecaadcc36e
commit ce8a78454f
14 changed files with 290 additions and 29 deletions

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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
View 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
}

View File

@ -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
Args []string
// Compile
SourceFileName string // put code when compile
CompiledFileNames []string // exec files
// limits
TimeLimit uint64

1
pkg/diff/diff.go Normal file
View File

@ -0,0 +1 @@
package diff

11
problem/interface.go Normal file
View 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)
}

View File

@ -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)
}

View File

@ -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

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

View File

@ -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

View File

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

View 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