@@ -26,6 +26,7 @@ import (
2626 ocispec "github.com/opencontainers/image-spec/specs-go/v1"
2727 "github.com/pkg/errors"
2828 "golang.org/x/sync/errgroup"
29+ "golang.org/x/sync/semaphore"
2930)
3031
3132var (
@@ -108,19 +109,30 @@ func Walk(ctx context.Context, handler Handler, descs ...ocispec.Descriptor) err
108109// handler may return `ErrSkipDesc` to signal to the dispatcher to not traverse
109110// any children.
110111//
112+ // A concurrency limiter can be passed in to limit the number of concurrent
113+ // handlers running. When limiter is nil, there is no limit.
114+ //
111115// Typically, this function will be used with `FetchHandler`, often composed
112116// with other handlers.
113117//
114118// If any handler returns an error, the dispatch session will be canceled.
115- func Dispatch (ctx context.Context , handler Handler , descs ... ocispec.Descriptor ) error {
119+ func Dispatch (ctx context.Context , handler Handler , limiter * semaphore. Weighted , descs ... ocispec.Descriptor ) error {
116120 eg , ctx := errgroup .WithContext (ctx )
117121 for _ , desc := range descs {
118122 desc := desc
119123
124+ if limiter != nil {
125+ if err := limiter .Acquire (ctx , 1 ); err != nil {
126+ return err
127+ }
128+ }
120129 eg .Go (func () error {
121130 desc := desc
122131
123132 children , err := handler .Handle (ctx , desc )
133+ if limiter != nil {
134+ limiter .Release (1 )
135+ }
124136 if err != nil {
125137 if errors .Cause (err ) == ErrSkipDesc {
126138 return nil // don't traverse the children.
@@ -129,7 +141,7 @@ func Dispatch(ctx context.Context, handler Handler, descs ...ocispec.Descriptor)
129141 }
130142
131143 if len (children ) > 0 {
132- return Dispatch (ctx , handler , children ... )
144+ return Dispatch (ctx , handler , limiter , children ... )
133145 }
134146
135147 return nil
0 commit comments