From 8518f0423c7f172f948c4ab5872a42ff1b3ef337 Mon Sep 17 00:00:00 2001 From: Mohammad Rajabloo Date: Sun, 12 Oct 2025 14:52:12 +0330 Subject: [PATCH 1/4] run providers concurrent function --- vabastegi.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/vabastegi.go b/vabastegi.go index fb7ba92..f87f77b 100644 --- a/vabastegi.go +++ b/vabastegi.go @@ -3,6 +3,8 @@ package vabastegi import ( "context" "time" + + "github.com/mrsoftware/errors" ) // Provider is a dependency provider for application. @@ -92,3 +94,18 @@ func (a *App[ـ]) Log(level logLevel, message string, args ...interface{}) { func (a *App[_]) Wait() error { return a.Lifecycle.Wait() } + +// Concurrent is if you need to run multiple provider concurrently. +func Concurrent[T any](providers ...Provider[T]) Provider[T] { + return func(ctx context.Context, app *App[T]) error { + wg := errors.NewWaitGroup(errors.WaitGroupWithContext(ctx)) + + for _, provider := range providers { + wg.DoWithContext(ctx, func(ctx context.Context) error { + return provider(ctx, app) + }) + } + + return wg.Wait() + } +} From 71b4fd36a4cd3dd7496abf726ecc45331ab5209e Mon Sep 17 00:00:00 2001 From: Mohammad Rajabloo Date: Mon, 13 Oct 2025 15:20:34 +0330 Subject: [PATCH 2/4] add withTimout option and withEventLogger option and fix concurrent provider name --- lifecycle.go | 9 ++++++++- options.go | 25 ++++++++++++++++++++++++- vabastegi.go | 8 +++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/lifecycle.go b/lifecycle.go index 433dcca..722a8f6 100644 --- a/lifecycle.go +++ b/lifecycle.go @@ -129,7 +129,14 @@ func getProviderName(creator interface{}, index int) string { return reference } - parts := strings.Split(reference, ".") + splitCount := -1 + + if strings.HasPrefix(reference, concurrentFuncRefName) { + reference = concurrentFuncRefName + splitCount = 3 + } + + parts := strings.SplitN(reference, ".", splitCount) return parts[len(parts)-(1+index)] } diff --git a/options.go b/options.go index 9983e27..4bc2ed5 100644 --- a/options.go +++ b/options.go @@ -1,6 +1,12 @@ package vabastegi -import "context" +import ( + "context" + "io" + "time" + + "github.com/mrsoftware/errors" +) // Options of Vabastegi. type Options struct { @@ -40,3 +46,20 @@ func WithContext(ctx context.Context) Option { options.Ctx = ctx } } + +// WithTimeout used if you need to pass timout for application booting. +func WithTimeout(timeout time.Duration) Option { + return func(options *Options) { + // ignoring cancel + ctx, _ := context.WithTimeoutCause(options.Ctx, timeout, errors.New("application timeout")) + + options.Ctx = ctx + } +} + +// WithEventLogger register event handlers for logger. +func WithEventLogger(writer io.Writer, level logLevel) Option { + return func(options *Options) { + options.EventHandlers = append(options.EventHandlers, NewEventLogger(writer, level)) + } +} diff --git a/vabastegi.go b/vabastegi.go index f87f77b..9608fc5 100644 --- a/vabastegi.go +++ b/vabastegi.go @@ -2,6 +2,8 @@ package vabastegi import ( "context" + "reflect" + "runtime" "time" "github.com/mrsoftware/errors" @@ -95,14 +97,18 @@ func (a *App[_]) Wait() error { return a.Lifecycle.Wait() } +var concurrentFuncRefName = runtime.FuncForPC(reflect.ValueOf(Concurrent[any]).Pointer()).Name() + // Concurrent is if you need to run multiple provider concurrently. func Concurrent[T any](providers ...Provider[T]) Provider[T] { return func(ctx context.Context, app *App[T]) error { wg := errors.NewWaitGroup(errors.WaitGroupWithContext(ctx)) for _, provider := range providers { + provider := provider + wg.DoWithContext(ctx, func(ctx context.Context) error { - return provider(ctx, app) + return app.Builds(provider) }) } From c840834c4043e96bb02126fd0e1e164b24b9934f Mon Sep 17 00:00:00 2001 From: Mohammad Rajabloo Date: Tue, 14 Oct 2025 21:17:43 +0330 Subject: [PATCH 3/4] make cancel cause available as error --- options.go | 5 ++++- vabastegi.go | 12 ++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/options.go b/options.go index 4bc2ed5..fc2c213 100644 --- a/options.go +++ b/options.go @@ -8,6 +8,9 @@ import ( "github.com/mrsoftware/errors" ) +// ErrApplicationTimeout . +var ErrApplicationTimeout = errors.New("application timeout") + // Options of Vabastegi. type Options struct { GracefulShutdown bool @@ -51,7 +54,7 @@ func WithContext(ctx context.Context) Option { func WithTimeout(timeout time.Duration) Option { return func(options *Options) { // ignoring cancel - ctx, _ := context.WithTimeoutCause(options.Ctx, timeout, errors.New("application timeout")) + ctx, _ := context.WithTimeoutCause(options.Ctx, timeout, ErrApplicationTimeout) options.Ctx = ctx } diff --git a/vabastegi.go b/vabastegi.go index 9608fc5..0e50ca0 100644 --- a/vabastegi.go +++ b/vabastegi.go @@ -55,11 +55,9 @@ func (a *App[T]) Builds(providers ...Provider[T]) (err error) { defer func() { a.Events.Publish(&OnApplicationShutdownExecuted{Runtime: time.Since(startAt), Err: err}) }() for _, provider := range providers { - if err = a.Build(a.Lifecycle.GetContext(), provider); err == nil { - continue + if err = a.Build(a.Lifecycle.GetContext(), provider); err != nil { + return err } - - return err } return nil @@ -67,6 +65,12 @@ func (a *App[T]) Builds(providers ...Provider[T]) (err error) { // Build use the provider to set a dependency. func (a *App[T]) Build(ctx context.Context, provider Provider[T]) (err error) { + select { + case <-ctx.Done(): + return context.Cause(ctx) + default: + } + startAt := time.Now() a.Events.Publish(&OnBuildExecuting{ From 11627159c9f241ad0c1926a110edd22b24bbf379 Mon Sep 17 00:00:00 2001 From: Mohammad Rajabloo Date: Tue, 14 Oct 2025 22:02:29 +0330 Subject: [PATCH 4/4] make cancel cause available in wait method --- lifecycle.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lifecycle.go b/lifecycle.go index 722a8f6..0ecdc99 100644 --- a/lifecycle.go +++ b/lifecycle.go @@ -42,7 +42,7 @@ func (l *lifecycle) Wait() error { var err error select { case <-l.ctx.Done(): - err = l.ctx.Err() + err = context.Cause(l.ctx) case err = <-errors.WaitChanel(l.waitGroup): l.Stop(err) }