package runner import ( "context" "git.0x7f.app/WOJ/woj-server/internal/e" "git.0x7f.app/WOJ/woj-server/internal/misc/config" "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/pkg/pool" "git.0x7f.app/WOJ/woj-server/pkg/utils" "github.com/containerd/containerd" "github.com/containerd/containerd/namespaces" "github.com/samber/do" "go.uber.org/zap" "runtime" "sync/atomic" ) var _ Service = (*service)(nil) type Service interface { // EnsureDeps build docker images EnsureDeps() e.Status // NewProblem = Download + Parse + Prebuild NewProblem(meta *JudgeMeta, url string, force bool) (*Config, e.Status) // Compile compile user submission Compile(meta *JudgeMeta) (*JudgeStatus, e.Status) // RunAndJudge execute user program RunAndJudge(meta *JudgeMeta) (*JudgeStatus, int32, e.Status) // ParseConfig parse config file ParseConfig(meta *JudgeMeta, skipCheck bool) (*Config, error) // ProblemExists check if problem exists ProblemExists(meta *JudgeMeta) bool HealthCheck() error Shutdown() error } func NewService(i *do.Injector) (Service, error) { concurrency := utils.If(runtime.NumCPU() > 1, runtime.NumCPU()-1, 1) cfg := do.MustInvoke[config.Service](i).GetConfig() srv := &service{ log: do.MustInvoke[log.Service](i).GetLogger("runner"), pool: pool.NewTaskPool(concurrency, concurrency), verbose: cfg.Development, } var err error srv.container.client, err = containerd.New(cfg.Runner.Address) if err != nil { srv.log.Error("failed to connect to containerd", zap.Error(err)) return nil, err } srv.container.ctx = namespaces.WithNamespace(context.Background(), "woj") srv.container.count.Store(0) srv.pool.Start() return srv, nil } type service struct { log *zap.Logger pool *pool.TaskPool container struct { client *containerd.Client ctx context.Context count atomic.Uint64 } verbose bool } func (s *service) HealthCheck() error { return nil } func (s *service) Shutdown() error { s.pool.Stop() if s.container.client != nil { // TODO: wait and kill all containers _ = s.container.client.Close() } return nil }