46 lines
1.3 KiB
Go
46 lines
1.3 KiB
Go
package dockerexec
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// D48 — a restore holds the image-work lock from its pre-pull until its last start. While it is held the clean-up
|
|
// cannot run, and a compose up inside the hold does not block.
|
|
func TestD48_HoldImageWorkBlocksCleanupAndNests(t *testing.T) {
|
|
end := HoldImageWork()
|
|
if _, ok := TryImageCleanup(); ok {
|
|
t.Fatal("the image clean-up must not run while a restore holds its images")
|
|
}
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
BeginImageWork([]string{"compose", "up", "-d"})()
|
|
}()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("a compose up inside the restore's hold must not block")
|
|
}
|
|
end()
|
|
unlock, ok := TryImageCleanup()
|
|
if !ok {
|
|
t.Fatal("after the hold the clean-up runs again")
|
|
}
|
|
unlock()
|
|
}
|
|
|
|
// The nesting is safe only while the one exclusive taker never WAITS (a waiting writer would block a nested RLock).
|
|
// Pin it: the package takes the lock exclusively nowhere but TryLock.
|
|
func TestD48_TheOnlyExclusiveTakerNeverWaits(t *testing.T) {
|
|
b, err := os.ReadFile("imagework.go")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(b), "imageWorkMu.Lock()") {
|
|
t.Fatal("a blocking exclusive Lock() on imageWorkMu would deadlock a restore's nested shared hold (HoldImageWork)")
|
|
}
|
|
}
|