Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ require (
golang.org/x/text v0.24.0
golang.org/x/tools v0.32.0
google.golang.org/protobuf v1.36.6
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
gopkg.in/yaml.v2 v2.4.0
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e
)
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -919,8 +919,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs=
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
62 changes: 58 additions & 4 deletions internal/server/endpoints/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
"sync"
"time"

tomb "gopkg.in/tomb.v2"

"github.com/lxc/incus/v6/internal/linux"
"github.com/lxc/incus/v6/internal/server/endpoints/listeners"
"github.com/lxc/incus/v6/internal/util"
Expand Down Expand Up @@ -157,7 +155,7 @@ func Up(config *Config) (*Endpoints, error) {
// Endpoints are in charge of bringing up and down the HTTP endpoints for
// serving the REST API.
type Endpoints struct {
tomb *tomb.Tomb // Controls the HTTP servers shutdown.
tomb *Tomb // Controls the HTTP servers shutdown.
mu sync.RWMutex // Serialize access to internal state.
listeners map[kind]net.Listener // Activer listeners by endpoint type.
servers map[kind]*http.Server // HTTP servers by endpoint type.
Expand Down Expand Up @@ -425,7 +423,7 @@ func (e *Endpoints) serve(kind kind) {
// Defer the creation of the tomb, so Down() doesn't wait on it unless
// we actually have spawned at least a server.
if e.tomb == nil {
e.tomb = &tomb.Tomb{}
e.tomb = &Tomb{}
}

e.tomb.Go(func() error {
Expand Down Expand Up @@ -501,3 +499,59 @@ var descriptions = map[kind]string{
vmvsock: "VM socket",
storageBuckets: "Storage buckets socket",
}

// Tomb tracks the lifecycle of one or more goroutines.
type Tomb struct {
wg sync.WaitGroup
count int
mutex sync.RWMutex
errOnce sync.Once
err error
}

func (g *Tomb) add(delta int) {
g.mutex.Lock()
defer g.mutex.Unlock()
g.count += delta
if g.count >= 0 {
g.wg.Add(delta)
}
}

// Go runs f in a new goroutine and tracks its termination.
func (g *Tomb) Go(f func() error) {
g.add(1)

go func() {
defer g.add(-1)

err := f()
if err != nil {
g.errOnce.Do(func() {
g.err = err
})
}
}()
}

// Kill marks all running goroutings done.
func (g *Tomb) Kill(err error) {
if err != nil {
g.errOnce.Do(func() {
g.err = err
})
}

g.mutex.RLock()
count := g.count
g.mutex.RUnlock()
if count != 0 {
g.add(-count)
}
}

// Wait blocks until all goroutines have finished running.
func (g *Tomb) Wait() error {
g.wg.Wait()
return g.err
}