Skip to content

Commit a18acaf

Browse files
committed
Avoid deadlock in unpacker.
Signed-off-by: Lantao Liu <[email protected]>
1 parent b5ede20 commit a18acaf

2 files changed

Lines changed: 44 additions & 3 deletions

File tree

pull.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (_ Ima
7070
}
7171
unpackWrapper, eg := u.handlerWrapper(ctx, &unpacks)
7272
defer func() {
73+
if retErr != nil {
74+
// Forcibly stop the unpacker if there is
75+
// an error.
76+
eg.Cancel()
77+
}
7378
if err := eg.Wait(); err != nil {
7479
if retErr == nil {
7580
retErr = errors.Wrap(err, "unpack")

unpacker.go

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,32 @@ func (u *unpacker) unpack(ctx context.Context, config ocispec.Descriptor, layers
186186
return nil
187187
}
188188

189-
func (u *unpacker) handlerWrapper(uctx context.Context, unpacks *int32) (func(images.Handler) images.Handler, *errgroup.Group) {
190-
eg, uctx := errgroup.WithContext(uctx)
189+
type errGroup struct {
190+
*errgroup.Group
191+
cancel context.CancelFunc
192+
}
193+
194+
func newErrGroup(ctx context.Context) (*errGroup, context.Context) {
195+
ctx, cancel := context.WithCancel(ctx)
196+
eg, ctx := errgroup.WithContext(ctx)
197+
return &errGroup{
198+
Group: eg,
199+
cancel: cancel,
200+
}, ctx
201+
}
202+
203+
func (e *errGroup) Cancel() {
204+
e.cancel()
205+
}
206+
207+
func (e *errGroup) Wait() error {
208+
err := e.Group.Wait()
209+
e.cancel()
210+
return err
211+
}
212+
213+
func (u *unpacker) handlerWrapper(uctx context.Context, unpacks *int32) (func(images.Handler) images.Handler, *errGroup) {
214+
eg, uctx := newErrGroup(uctx)
191215
return func(f images.Handler) images.Handler {
192216
var (
193217
lock sync.Mutex
@@ -234,7 +258,19 @@ func (u *unpacker) handlerWrapper(uctx context.Context, unpacks *int32) (func(im
234258
update := !schema1
235259
lock.Unlock()
236260
if update {
237-
u.updateCh <- desc
261+
select {
262+
case <-uctx.Done():
263+
// Do not send update if unpacker is not running.
264+
default:
265+
select {
266+
case u.updateCh <- desc:
267+
case <-uctx.Done():
268+
// Do not send update if unpacker is not running.
269+
}
270+
}
271+
// Checking ctx.Done() prevents the case that unpacker
272+
// exits unexpectedly, but update continues to be generated,
273+
// and eventually fills up updateCh and blocks forever.
238274
}
239275
}
240276
return children, nil

0 commit comments

Comments
 (0)