aboutsummaryrefslogtreecommitdiff
path: root/manager/manager.go
diff options
context:
space:
mode:
authorLibravatar Terin Stock <terinjokes@gmail.com>2023-03-02 00:05:26 +0100
committerLibravatar Terin Stock <terinjokes@gmail.com>2023-03-02 00:05:26 +0100
commitd6baaceefab0a307b1d7ae8f8bf92cfa5e03a854 (patch)
tree856ef72e83f4c30e5c1a18a2c8ebb596d24f60e8 /manager/manager.go
parentadd initial version of cgit-httpd (diff)
downloadcgit-httpd-d6baaceefab0a307b1d7ae8f8bf92cfa5e03a854.tar.xz
fix: manager started race
It's possible for there to be a data race where `Add` checks if the manager has started, then proceeds to append to the runnables slice, at the same time that `Start` is progressing towards starting the manager. This results in a data race with the runnables slice. This changeset modifies the manager to use a more traditional locking approach.
Diffstat (limited to 'manager/manager.go')
-rw-r--r--manager/manager.go21
1 files changed, 17 insertions, 4 deletions
diff --git a/manager/manager.go b/manager/manager.go
index 1a79214..704bced 100644
--- a/manager/manager.go
+++ b/manager/manager.go
@@ -5,13 +5,15 @@ package manager
import (
"context"
- "sync/atomic"
+ "errors"
+ "sync"
"golang.org/x/sync/errgroup"
)
type Manager struct {
- started uint32
+ mu sync.Mutex
+ started bool
runnables []Runnable
internalCtx context.Context
group *errgroup.Group
@@ -24,7 +26,10 @@ func New() *Manager {
}
func (m *Manager) Add(r Runnable) error {
- if atomic.LoadUint32(&m.started) == 1 {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ if m.started {
m.group.Go(func() error {
return r.Start(m.internalCtx)
})
@@ -36,11 +41,16 @@ func (m *Manager) Add(r Runnable) error {
}
func (m *Manager) Start(ctx context.Context) error {
+ m.mu.Lock()
+ if m.started {
+ m.mu.Unlock()
+ return errors.New("manager already started")
+ }
+
g, internalCtx := errgroup.WithContext(ctx)
m.group = g
m.internalCtx = internalCtx
- atomic.SwapUint32(&m.started, 1)
for _, r := range m.runnables {
r := r
g.Go(func() error {
@@ -48,6 +58,9 @@ func (m *Manager) Start(ctx context.Context) error {
})
}
m.runnables = nil
+ m.started = true
+
+ m.mu.Unlock()
return g.Wait()
}