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