You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
4.1 KiB
Go
180 lines
4.1 KiB
Go
![]()
2 years ago
|
package walker
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"sync"
|
||
|
|
||
|
solverpb "github.com/moby/buildkit/solver/pb"
|
||
|
"github.com/pkg/errors"
|
||
|
)
|
||
|
|
||
|
// Controller is a utility to control walkers with debugger-like interface like "continue" and "next".
|
||
|
type Controller struct {
|
||
|
def *solverpb.Definition
|
||
|
breakpoints *Breakpoints
|
||
|
|
||
|
breakHandler BreakHandlerFunc
|
||
|
onVertexHandler OnVertexHandlerFunc
|
||
|
onWalkDoneFunc func(error)
|
||
|
|
||
|
walker *Walker
|
||
|
walkerMu sync.Mutex
|
||
|
walkCancel func()
|
||
|
|
||
|
curStepDoneCh chan struct{}
|
||
|
|
||
|
curWalkErrCh chan error
|
||
|
curWalkDoneCh chan struct{}
|
||
|
}
|
||
|
|
||
|
// Status is a status of the controller.
|
||
|
type Status struct {
|
||
|
|
||
|
// Definition is the target definition where walking is performed.
|
||
|
Definition *solverpb.Definition
|
||
|
|
||
|
// Cursors is current cursor positions on the walker.
|
||
|
Cursors []solverpb.Range
|
||
|
}
|
||
|
|
||
|
// NewController returns a walker controller.
|
||
|
func NewController(def *solverpb.Definition, breakpoints *Breakpoints, breakHandler BreakHandlerFunc, onVertexHandler OnVertexHandlerFunc, onWalkDoneFunc func(error)) *Controller {
|
||
|
return &Controller{
|
||
|
def: def,
|
||
|
breakpoints: breakpoints,
|
||
|
breakHandler: breakHandler,
|
||
|
onVertexHandler: onVertexHandler,
|
||
|
onWalkDoneFunc: onWalkDoneFunc,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Breakpoint returns a set of breakpoints currently recognized.
|
||
|
func (c *Controller) Breakpoints() *Breakpoints {
|
||
|
return c.breakpoints
|
||
|
}
|
||
|
|
||
|
// Inspect returns the current status.
|
||
|
func (c *Controller) Inspect() *Status {
|
||
|
c.walkerMu.Lock()
|
||
|
defer c.walkerMu.Unlock()
|
||
|
var cursors []solverpb.Range
|
||
|
if c.walker != nil {
|
||
|
cursors = c.walker.GetCursors()
|
||
|
}
|
||
|
return &Status{
|
||
|
Definition: c.def,
|
||
|
Cursors: cursors,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// IsStarted returns true when there is an on-going walker. Returns false otherwise.
|
||
|
func (c *Controller) IsStarted() bool {
|
||
|
c.walkerMu.Lock()
|
||
|
w := c.walker
|
||
|
c.walkerMu.Unlock()
|
||
|
return w != nil
|
||
|
}
|
||
|
|
||
|
// StartWalk starts walking in a gorouitne. This function returns immediately without waiting for the
|
||
|
// completion of the walking. Parallel invoking of this method isn't supported and an error will be returned
|
||
|
// if there is an on-going walker. Previous walking must be canceled using WalkCancel method.
|
||
|
func (c *Controller) StartWalk() error {
|
||
|
c.walkerMu.Lock()
|
||
|
if c.walker != nil {
|
||
|
c.walkerMu.Unlock()
|
||
|
return errors.Errorf("walker already running")
|
||
|
}
|
||
|
c.walkerMu.Unlock()
|
||
|
|
||
|
go func() {
|
||
|
w := NewWalker(c.breakpoints, func(ctx context.Context, bCtx *BreakContext) error {
|
||
|
if err := c.breakHandler(ctx, bCtx); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
curStepDoneCh := make(chan struct{})
|
||
|
c.curStepDoneCh = curStepDoneCh
|
||
|
select {
|
||
|
case <-curStepDoneCh:
|
||
|
case <-ctx.Done():
|
||
|
return ctx.Err()
|
||
|
}
|
||
|
return nil
|
||
|
}, c.onVertexHandler)
|
||
|
|
||
|
c.walkerMu.Lock()
|
||
|
c.walker = w
|
||
|
c.walkerMu.Unlock()
|
||
|
|
||
|
ctx, cancel := context.WithCancel(context.TODO())
|
||
|
c.walkCancel = cancel
|
||
|
c.curWalkErrCh = make(chan error)
|
||
|
c.curWalkDoneCh = make(chan struct{})
|
||
|
err := w.Walk(ctx, c.def)
|
||
|
c.onWalkDoneFunc(err)
|
||
|
w.Close()
|
||
|
|
||
|
c.walkerMu.Lock()
|
||
|
c.walker = nil
|
||
|
c.walkerMu.Unlock()
|
||
|
|
||
|
if err != nil {
|
||
|
c.curWalkErrCh <- err
|
||
|
}
|
||
|
close(c.curWalkDoneCh)
|
||
|
}()
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// WalkCancel cancels on-going walking.
|
||
|
func (c *Controller) WalkCancel() error {
|
||
|
c.walkerMu.Lock()
|
||
|
if c.walker != nil {
|
||
|
c.walker = nil
|
||
|
c.walkerMu.Unlock()
|
||
|
c.walkCancel()
|
||
|
select {
|
||
|
case err := <-c.curWalkErrCh:
|
||
|
return err
|
||
|
case <-c.curWalkDoneCh:
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
c.walkerMu.Unlock()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Continue resumes the walker. The walker will stop at the next breakpoint.
|
||
|
func (c *Controller) Continue() {
|
||
|
c.walkerMu.Lock()
|
||
|
defer c.walkerMu.Unlock()
|
||
|
if c.walker != nil {
|
||
|
c.walker.BreakAllNode(false)
|
||
|
}
|
||
|
if c.curStepDoneCh != nil {
|
||
|
close(c.curStepDoneCh)
|
||
|
c.curStepDoneCh = nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Next resumes the walker. The walker will stop at the next vertex.
|
||
|
func (c *Controller) Next() error {
|
||
|
c.walkerMu.Lock()
|
||
|
defer c.walkerMu.Unlock()
|
||
|
if c.walker != nil {
|
||
|
c.walker.BreakAllNode(true)
|
||
|
} else {
|
||
|
return errors.Errorf("walker isn't running")
|
||
|
}
|
||
|
if c.curStepDoneCh != nil {
|
||
|
close(c.curStepDoneCh)
|
||
|
c.curStepDoneCh = nil
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Close closes this controller.
|
||
|
func (c *Controller) Close() error {
|
||
|
return c.WalkCancel()
|
||
|
}
|