make sure execerror is released on cancellation
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>v0.8
parent
372df78cc8
commit
df3a9cad23
|
@ -659,6 +659,7 @@ func (s *sharedOp) CalcSlowCache(ctx context.Context, index Index, p PreprocessF
|
|||
case <-ctx.Done():
|
||||
if strings.Contains(err.Error(), context.Canceled.Error()) {
|
||||
complete = false
|
||||
releaseError(err)
|
||||
err = errors.Wrap(ctx.Err(), err.Error())
|
||||
}
|
||||
default:
|
||||
|
@ -717,6 +718,7 @@ func (s *sharedOp) CacheMap(ctx context.Context, index int) (resp *cacheMapResp,
|
|||
case <-ctx.Done():
|
||||
if strings.Contains(err.Error(), context.Canceled.Error()) {
|
||||
complete = false
|
||||
releaseError(err)
|
||||
err = errors.Wrap(ctx.Err(), err.Error())
|
||||
}
|
||||
default:
|
||||
|
@ -774,6 +776,7 @@ func (s *sharedOp) Exec(ctx context.Context, inputs []Result) (outputs []Result,
|
|||
case <-ctx.Done():
|
||||
if strings.Contains(err.Error(), context.Canceled.Error()) {
|
||||
complete = false
|
||||
releaseError(err)
|
||||
err = errors.Wrap(ctx.Err(), err.Error())
|
||||
}
|
||||
default:
|
||||
|
@ -911,3 +914,15 @@ func WrapSlowCache(err error, index Index, res Result) error {
|
|||
}
|
||||
return &SlowCacheError{Index: index, Result: res, error: err}
|
||||
}
|
||||
|
||||
func releaseError(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if re, ok := err.(interface {
|
||||
Release() error
|
||||
}); ok {
|
||||
re.Release()
|
||||
}
|
||||
releaseError(errors.Unwrap(err))
|
||||
}
|
||||
|
|
|
@ -167,6 +167,8 @@ func newResultProxy(b *llbBridge, req frontend.SolveRequest) *resultProxy {
|
|||
rp.errResults = append(rp.errResults, res)
|
||||
return nil
|
||||
})
|
||||
// acquire ownership so ExecError finalizer doesn't attempt to release as well
|
||||
ee.OwnerBorrowed = true
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package errdefs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
|
||||
"github.com/moby/buildkit/solver"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ExecError will be returned when an error is encountered when evaluating an op.
|
||||
|
@ -9,6 +13,7 @@ type ExecError struct {
|
|||
error
|
||||
Inputs []solver.Result
|
||||
Mounts []solver.Result
|
||||
OwnerBorrowed bool
|
||||
}
|
||||
|
||||
func (e *ExecError) Unwrap() error {
|
||||
|
@ -35,13 +40,35 @@ func (e *ExecError) EachRef(fn func(solver.Result) error) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
func (e *ExecError) Release() error {
|
||||
if e.OwnerBorrowed {
|
||||
return nil
|
||||
}
|
||||
err := e.EachRef(func(r solver.Result) error {
|
||||
r.Release(context.TODO())
|
||||
return nil
|
||||
})
|
||||
e.OwnerBorrowed = true
|
||||
return err
|
||||
}
|
||||
|
||||
func WithExecError(err error, inputs, mounts []solver.Result) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &ExecError{
|
||||
ee := &ExecError{
|
||||
error: err,
|
||||
Inputs: inputs,
|
||||
Mounts: mounts,
|
||||
}
|
||||
runtime.SetFinalizer(ee, func(e *ExecError) {
|
||||
if !e.OwnerBorrowed {
|
||||
e.EachRef(func(r solver.Result) error {
|
||||
logrus.Warn("leaked execError detected and released")
|
||||
r.Release(context.TODO())
|
||||
return nil
|
||||
})
|
||||
}
|
||||
})
|
||||
return ee
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue