up: fix write/close race condition in logPrinter

The code used an atomic bool to guard channel writes. However, this
failed to synchronize with the call to close(), causing a panic.

Fix the race condition by using a mutex to guard the update to the
bool `stopped` and subsequent channel writes. This ensures atomic
execution of both updates to `stopped` and channel writes, preventing
races between writes and close().

Signed-off-by: horus <horus.li@gmail.com>
This commit is contained in:
horus 2023-12-19 21:55:04 +08:00 committed by Nicolas De loof
parent aefc2a111a
commit 1baa4f4489

View File

@ -18,7 +18,7 @@ package compose
import ( import (
"fmt" "fmt"
"sync/atomic" "sync"
"github.com/docker/compose/v2/pkg/api" "github.com/docker/compose/v2/pkg/api"
) )
@ -32,9 +32,10 @@ type logPrinter interface {
} }
type printer struct { type printer struct {
sync.Mutex
queue chan api.ContainerEvent queue chan api.ContainerEvent
consumer api.LogConsumer consumer api.LogConsumer
stopped atomic.Bool stopped bool
} }
// newLogPrinter builds a LogPrinter passing containers logs to LogConsumer // newLogPrinter builds a LogPrinter passing containers logs to LogConsumer
@ -53,16 +54,21 @@ func (p *printer) Cancel() {
} }
func (p *printer) Stop() { func (p *printer) Stop() {
if p.stopped.CompareAndSwap(false, true) { p.Lock()
defer p.Unlock()
if !p.stopped {
// only close if this is the first call to stop // only close if this is the first call to stop
p.stopped = true
close(p.queue) close(p.queue)
} }
} }
func (p *printer) HandleEvent(event api.ContainerEvent) { func (p *printer) HandleEvent(event api.ContainerEvent) {
// prevent deadlocking, if the printer is done, there's no reader for p.Lock()
// queue, so this write could block indefinitely defer p.Unlock()
if p.stopped.Load() { if p.stopped {
// prevent deadlocking, if the printer is done, there's no reader for
// queue, so this write could block indefinitely
return return
} }
p.queue <- event p.queue <- event