@@ -5,10 +5,9 @@ import (
5
5
"net"
6
6
"net/http"
7
7
"sync"
8
+ "sync/atomic"
8
9
"time"
9
10
10
- tomb "gopkg.in/tomb.v2"
11
-
12
11
"github.com/lxc/incus/v6/internal/linux"
13
12
"github.com/lxc/incus/v6/internal/server/endpoints/listeners"
14
13
"github.com/lxc/incus/v6/internal/util"
@@ -157,7 +156,7 @@ func Up(config *Config) (*Endpoints, error) {
157
156
// Endpoints are in charge of bringing up and down the HTTP endpoints for
158
157
// serving the REST API.
159
158
type Endpoints struct {
160
- tomb * tomb. Tomb // Controls the HTTP servers shutdown.
159
+ tomb * Tomb // Controls the HTTP servers shutdown.
161
160
mu sync.RWMutex // Serialize access to internal state.
162
161
listeners map [kind ]net.Listener // Activer listeners by endpoint type.
163
162
servers map [kind ]* http.Server // HTTP servers by endpoint type.
@@ -425,7 +424,7 @@ func (e *Endpoints) serve(kind kind) {
425
424
// Defer the creation of the tomb, so Down() doesn't wait on it unless
426
425
// we actually have spawned at least a server.
427
426
if e .tomb == nil {
428
- e .tomb = & tomb. Tomb {}
427
+ e .tomb = & Tomb {}
429
428
}
430
429
431
430
e .tomb .Go (func () error {
@@ -501,3 +500,50 @@ var descriptions = map[kind]string{
501
500
vmvsock : "VM socket" ,
502
501
storageBuckets : "Storage buckets socket" ,
503
502
}
503
+
504
+ // Tomb tracks the lifecycle of one or more goroutines.
505
+ type Tomb struct {
506
+ count atomic.Int32
507
+ wg sync.WaitGroup
508
+ errOnce sync.Once
509
+ err error
510
+ }
511
+
512
+ func (g * Tomb ) add (delta int ) {
513
+ g .wg .Add (delta )
514
+ g .count .Add (int32 (delta ))
515
+ }
516
+
517
+ // Go runs f in a new goroutine and tracks its termination.
518
+ func (g * Tomb ) Go (f func () error ) {
519
+ g .add (1 )
520
+
521
+ go func () {
522
+ defer g .add (- 1 )
523
+ if err := f (); err != nil {
524
+ g .errOnce .Do (func () {
525
+ g .err = err
526
+ })
527
+ }
528
+ }()
529
+ }
530
+
531
+ // Kill marks all running goroutings done.
532
+ func (g * Tomb ) Kill (err error ) {
533
+ if err != nil {
534
+ g .errOnce .Do (func () {
535
+ g .err = err
536
+ })
537
+ }
538
+
539
+ c := int (g .count .Load ())
540
+ if c > 0 {
541
+ g .add (- c )
542
+ }
543
+ }
544
+
545
+ // Wait blocks until all goroutines have finished running.
546
+ func (g * Tomb ) Wait () error {
547
+ g .wg .Wait ()
548
+ return g .err
549
+ }
0 commit comments