diff --git a/file/interface.go b/file/interface.go index b29b9aa..0a7512d 100644 --- a/file/interface.go +++ b/file/interface.go @@ -2,16 +2,26 @@ package file import "os" +// Opener opens the file in readonly mode +// caller should close afterwards +type Opener interface { + Open() (*os.File, error) +} + +// Contenter reads the file content +type Contenter interface { + Content() ([]byte, error) +} + +// Namer get the file name +type Namer interface { + Name() string +} + // File defines file name with its content // file could on file system or memory type File interface { - // Name get the file name - Name() string - - // Content reads the file content - Content() ([]byte, error) - - // Open opens the file in readonly mode - // caller should close afterwards - Open() (*os.File, error) + Opener + Contenter + Namer } diff --git a/go.mod b/go.mod index 1b6867e..bc70741 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/criyle/go-judge -go 1.12 +go 1.13 -require github.com/criyle/go-sandbox v0.0.0-20190929004305-4001c7c7671f +require github.com/criyle/go-sandbox v0.0.0-20191014070058-6e46a732897f diff --git a/go.sum b/go.sum index badf642..82faeb2 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/criyle/go-sandbox v0.0.0-20190929004305-4001c7c7671f h1:2VJ5GhYvEEXOwIeRF2SNepXp5TQ98ZjELGIriGWQIps= -github.com/criyle/go-sandbox v0.0.0-20190929004305-4001c7c7671f/go.mod h1:xAafqqJsAlGuL8gMgnrbNtYLmCtAWzOZQ4kM9y6/zdk= +github.com/criyle/go-sandbox v0.0.0-20191014070058-6e46a732897f h1:5PNJaA2V/NVR98BegmbYW2g5pHdiX3b1iXdaV/fPA9M= +github.com/criyle/go-sandbox v0.0.0-20191014070058-6e46a732897f/go.mod h1:q6ywqlRA0V2VxWo9E+dQkzK2s5mHyq2GXHtF8oZq+mI= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -golang.org/x/sys v0.0.0-20190927073244-c990c680b611 h1:q9u40nxWT5zRClI/uU9dHCiYGottAg6Nzz4YUQyHxdA= -golang.org/x/sys v0.0.0-20190927073244-c990c680b611/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/pkg/runner/run.go b/pkg/runner/run.go new file mode 100644 index 0000000..75c10db --- /dev/null +++ b/pkg/runner/run.go @@ -0,0 +1 @@ +package runner diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go new file mode 100644 index 0000000..753b6cf --- /dev/null +++ b/pkg/runner/runner.go @@ -0,0 +1,59 @@ +package runner + +import ( + "github.com/criyle/go-judge/file" + "github.com/criyle/go-sandbox/daemon" +) + +// Pool implements pool of daemons +type Pool interface { + Get() (*daemon.Master, error) + Put(*daemon.Master) +} + +// PipeCollector can be used in Cmd.Files paramenter +type PipeCollector struct { + Name string + SizeLimit uint64 +} + +// Pipe defines the pipe between parallel Cmd +type Pipe struct { + ReadIndex int + ReadFd int + WriteIndex int + WriteFd int +} + +// Cmd defines instruction to run a program in daemon +type Cmd struct { + // argument, environment + Args []string + Env []string + + // fds for exec: can be nil, file.Opener, PipeCollector + // nil: undefined, will be closed + // file.Opener: will be opened and passed to runner + // PipeCollector: a pipe write end will be passed to runner and collected as a copyout file + Files []interface{} + + // cgroup limits + MemoryLimit uint64 + PidLimit uint64 + + // file contents to copyin before exec + CopyIn map[string]file.File + + // file names to copyout after exec + CopyOut []string +} + +// Runner defines the running instruction to run multiple +// Exec in parallel restricted within cgroup +type Runner struct { + CgroupPrefix string + Pool Pool + + Cmds []Cmd + Pipes []Pipe +} diff --git a/runner/pool.go b/runner/pool.go index d30a1bc..f85d015 100644 --- a/runner/pool.go +++ b/runner/pool.go @@ -7,14 +7,14 @@ import ( ) type pool struct { - builder *daemon.Builder + builder DaemonBuilder queue chan *daemon.Master count int32 } const maxPoolSize = 64 -func newPool(builder *daemon.Builder) *pool { +func newPool(builder DaemonBuilder) *pool { return &pool{ queue: make(chan *daemon.Master, maxPoolSize), builder: builder, diff --git a/runner/run.go b/runner/run.go index 055c875..31a3992 100644 --- a/runner/run.go +++ b/runner/run.go @@ -77,7 +77,7 @@ func (r *Runner) run(done <-chan struct{}, task *types.RunTask) *types.RunTaskRe // set running parameters execParam := daemon.ExecveParam{ Args: param.Args, - Envv: env, + Env: env, Fds: []uintptr{inputFile.Fd(), outputPipe.W.Fd(), errorPipe.W.Fd()}, SyncFunc: cg.AddProc, } @@ -130,10 +130,16 @@ func (r *Runner) run(done <-chan struct{}, task *types.RunTask) *types.RunTaskRe timeLimit = time.Duration(task.TimeLimit) * time.Millisecond } + // Wait for result + var rt stypes.Result + finish := make(chan struct{}) + go func() { + defer close(finish) + rt = <-rc + }() + var lastCPUUsage uint64 var totalTime time.Duration - var rt stypes.Result - var rtReceived bool lastCheckTime := time.Now() // wait task finish @@ -157,8 +163,7 @@ loop: lastCheckTime = now lastCPUUsage = cpuUsage - case rt = <-rc: // returned - rtReceived = true + case <-finish: // returned break loop case <-outputPipe.Done: // outputlimit exceeded @@ -171,9 +176,7 @@ loop: // get result if did not received cancelC.cancel() - if !rtReceived { - rt = <-rc - } + <-finish // generate resource usage cpuUsage, err := cg.CpuacctUsage() diff --git a/runner/runner.go b/runner/runner.go index 5e9661f..41499d3 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -8,13 +8,18 @@ import ( "github.com/criyle/go-sandbox/daemon" ) +// DaemonBuilder defines the abstract builder for daemon +type DaemonBuilder interface { + Build() (*daemon.Master, error) +} + // Runner is the task runner type Runner struct { // Queue is the message queue to receive run tasks Queue taskqueue.Receiver // Builder builds the sandbox runner - Builder *daemon.Builder + Builder DaemonBuilder Language language.Language