Use cached state instead of runc state.

Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
Lantao Liu 2019-10-02 21:32:36 -07:00
parent a83ae30016
commit 18be6e3714
6 changed files with 75 additions and 27 deletions

View File

@ -69,3 +69,7 @@ func (s *deletedState) SetExited(status int) {
func (s *deletedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { func (s *deletedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) {
return nil, errors.Errorf("cannot exec in a deleted state") return nil, errors.Errorf("cannot exec in a deleted state")
} }
func (s *deletedState) Status(ctx context.Context) (string, error) {
return "stopped", nil
}

View File

@ -261,17 +261,5 @@ func (e *execProcess) Status(ctx context.Context) (string, error) {
} }
e.mu.Lock() e.mu.Lock()
defer e.mu.Unlock() defer e.mu.Unlock()
// if we don't have a pid(pid=0) then the exec process has just been created return e.execState.Status(ctx)
if e.pid.get() == 0 {
return "created", nil
}
if e.pid.get() == StoppedPID {
return "stopped", nil
}
// if we have a pid and it can be signaled, the process is running
if err := unix.Kill(e.pid.get(), 0); err == nil {
return "running", nil
}
// else if we have a pid but it can nolonger be signaled, it has stopped
return "stopped", nil
} }

View File

@ -31,6 +31,7 @@ type execState interface {
Delete(context.Context) error Delete(context.Context) error
Kill(context.Context, uint32, bool) error Kill(context.Context, uint32, bool) error
SetExited(int) SetExited(int)
Status(context.Context) (string, error)
} }
type execCreatedState struct { type execCreatedState struct {
@ -82,6 +83,10 @@ func (s *execCreatedState) SetExited(status int) {
} }
} }
func (s *execCreatedState) Status(ctx context.Context) (string, error) {
return "created", nil
}
type execRunningState struct { type execRunningState struct {
p *execProcess p *execProcess
} }
@ -120,6 +125,10 @@ func (s *execRunningState) SetExited(status int) {
} }
} }
func (s *execRunningState) Status(ctx context.Context) (string, error) {
return "running", nil
}
type execStoppedState struct { type execStoppedState struct {
p *execProcess p *execProcess
} }
@ -157,3 +166,7 @@ func (s *execStoppedState) Kill(ctx context.Context, sig uint32, all bool) error
func (s *execStoppedState) SetExited(status int) { func (s *execStoppedState) SetExited(status int) {
// no op // no op
} }
func (s *execStoppedState) Status(ctx context.Context) (string, error) {
return "stopped", nil
}

View File

@ -62,6 +62,8 @@ type Init struct {
Platform stdio.Platform Platform stdio.Platform
io *processIO io *processIO
runtime *runc.Runc runtime *runc.Runc
// pausing preserves the pausing state.
pausing *atomicBool
status int status int
exited time.Time exited time.Time
pid safePid pid safePid
@ -97,6 +99,7 @@ func New(id string, runtime *runc.Runc, stdio stdio.Stdio) *Init {
p := &Init{ p := &Init{
id: id, id: id,
runtime: runtime, runtime: runtime,
pausing: new(atomicBool),
stdio: stdio, stdio: stdio,
status: 0, status: 0,
waitBlock: make(chan struct{}), waitBlock: make(chan struct{}),
@ -237,17 +240,14 @@ func (p *Init) ExitedAt() time.Time {
// Status of the process // Status of the process
func (p *Init) Status(ctx context.Context) (string, error) { func (p *Init) Status(ctx context.Context) (string, error) {
if p.pausing.get() {
return "pausing", nil
}
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock() defer p.mu.Unlock()
c, err := p.runtime.State(ctx, p.id) return p.initState.Status(ctx)
if err != nil {
if strings.Contains(err.Error(), "does not exist") {
return "stopped", nil
}
return "", p.runtimeError(err, "OCI runtime state failed")
}
return c.Status, nil
} }
// Start the init process // Start the init process

View File

@ -37,6 +37,7 @@ type initState interface {
Exec(context.Context, string, *ExecConfig) (Process, error) Exec(context.Context, string, *ExecConfig) (Process, error)
Kill(context.Context, uint32, bool) error Kill(context.Context, uint32, bool) error
SetExited(int) SetExited(int)
Status(context.Context) (string, error)
} }
type createdState struct { type createdState struct {
@ -103,6 +104,10 @@ func (s *createdState) Exec(ctx context.Context, path string, r *ExecConfig) (Pr
return s.p.exec(ctx, path, r) return s.p.exec(ctx, path, r)
} }
func (s *createdState) Status(ctx context.Context) (string, error) {
return "created", nil
}
type createdCheckpointState struct { type createdCheckpointState struct {
p *Init p *Init
opts *runc.RestoreOpts opts *runc.RestoreOpts
@ -211,6 +216,10 @@ func (s *createdCheckpointState) Exec(ctx context.Context, path string, r *ExecC
return nil, errors.Errorf("cannot exec in a created state") return nil, errors.Errorf("cannot exec in a created state")
} }
func (s *createdCheckpointState) Status(ctx context.Context) (string, error) {
return "created", nil
}
type runningState struct { type runningState struct {
p *Init p *Init
} }
@ -228,6 +237,13 @@ func (s *runningState) transition(name string) error {
} }
func (s *runningState) Pause(ctx context.Context) error { func (s *runningState) Pause(ctx context.Context) error {
s.p.pausing.set(true)
// NOTE "pausing" will be returned in the short window
// after `transition("paused")`, before `pausing` is reset
// to false. That doesn't break the state machine, just
// delays the "paused" state a little bit.
defer s.p.pausing.set(false)
if err := s.p.runtime.Pause(ctx, s.p.id); err != nil { if err := s.p.runtime.Pause(ctx, s.p.id); err != nil {
return s.p.runtimeError(err, "OCI runtime pause failed") return s.p.runtimeError(err, "OCI runtime pause failed")
} }
@ -271,6 +287,10 @@ func (s *runningState) Exec(ctx context.Context, path string, r *ExecConfig) (Pr
return s.p.exec(ctx, path, r) return s.p.exec(ctx, path, r)
} }
func (s *runningState) Status(ctx context.Context) (string, error) {
return "running", nil
}
type pausedState struct { type pausedState struct {
p *Init p *Init
} }
@ -335,6 +355,10 @@ func (s *pausedState) Exec(ctx context.Context, path string, r *ExecConfig) (Pro
return nil, errors.Errorf("cannot exec in a paused state") return nil, errors.Errorf("cannot exec in a paused state")
} }
func (s *pausedState) Status(ctx context.Context) (string, error) {
return "paused", nil
}
type stoppedState struct { type stoppedState struct {
p *Init p *Init
} }
@ -387,3 +411,7 @@ func (s *stoppedState) SetExited(status int) {
func (s *stoppedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { func (s *stoppedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) {
return nil, errors.Errorf("cannot exec in a stopped state") return nil, errors.Errorf("cannot exec in a stopped state")
} }
func (s *stoppedState) Status(ctx context.Context) (string, error) {
return "stopped", nil
}

View File

@ -27,6 +27,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
@ -62,6 +63,20 @@ func (s *safePid) set(pid int) {
s.Unlock() s.Unlock()
} }
type atomicBool int32
func (ab *atomicBool) set(b bool) {
if b {
atomic.StoreInt32((*int32)(ab), 1)
} else {
atomic.StoreInt32((*int32)(ab), 0)
}
}
func (ab *atomicBool) get() bool {
return atomic.LoadInt32((*int32)(ab)) == 1
}
// TODO(mlaventure): move to runc package? // TODO(mlaventure): move to runc package?
func getLastRuntimeError(r *runc.Runc) (string, error) { func getLastRuntimeError(r *runc.Runc) (string, error) {
if r.Log == "" { if r.Log == "" {