@@ -32,18 +32,19 @@ type logPrinter interface {
32
32
}
33
33
34
34
type printer struct {
35
- sync.Mutex
36
35
queue chan api.ContainerEvent
37
36
consumer api.LogConsumer
38
- stopped bool
37
+ stopCh chan struct {} // stopCh is a signal channel for producers to stop sending events to the queue
38
+ stop sync.Once
39
39
}
40
40
41
41
// newLogPrinter builds a LogPrinter passing containers logs to LogConsumer
42
42
func newLogPrinter (consumer api.LogConsumer ) logPrinter {
43
- queue := make (chan api.ContainerEvent )
44
43
printer := printer {
45
44
consumer : consumer ,
46
- queue : queue ,
45
+ queue : make (chan api.ContainerEvent ),
46
+ stopCh : make (chan struct {}),
47
+ stop : sync.Once {},
47
48
}
48
49
return & printer
49
50
}
@@ -54,24 +55,27 @@ func (p *printer) Cancel() {
54
55
}
55
56
56
57
func (p * printer ) Stop () {
57
- p .Lock ()
58
- defer p .Unlock ()
59
- if ! p .stopped {
60
- // only close if this is the first call to stop
61
- p .stopped = true
62
- close (p .queue )
63
- }
58
+ p .stop .Do (func () {
59
+ close (p .stopCh )
60
+ for {
61
+ select {
62
+ case <- p .queue :
63
+ // purge the queue to free producers goroutines
64
+ // p.queue will be garbage collected
65
+ default :
66
+ return
67
+ }
68
+ }
69
+ })
64
70
}
65
71
66
72
func (p * printer ) HandleEvent (event api.ContainerEvent ) {
67
- p .Lock ()
68
- defer p .Unlock ()
69
- if p .stopped {
70
- // prevent deadlocking, if the printer is done, there's no reader for
71
- // queue, so this write could block indefinitely
73
+ select {
74
+ case <- p .stopCh :
72
75
return
76
+ default :
77
+ p .queue <- event
73
78
}
74
- p .queue <- event
75
79
}
76
80
77
81
//nolint:gocyclo
@@ -80,58 +84,64 @@ func (p *printer) Run(cascadeStop bool, exitCodeFrom string, stopFn func() error
80
84
aborting bool
81
85
exitCode int
82
86
)
87
+ defer p .Stop ()
88
+
83
89
containers := map [string ]struct {}{}
84
- for event := range p .queue {
85
- container , id := event .Container , event .ID
86
- switch event .Type {
87
- case api .UserCancel :
88
- aborting = true
89
- case api .ContainerEventAttach :
90
- if _ , ok := containers [id ]; ok {
91
- continue
92
- }
93
- containers [id ] = struct {}{}
94
- p .consumer .Register (container )
95
- case api .ContainerEventExit , api .ContainerEventStopped , api .ContainerEventRecreated :
96
- if ! event .Restarting {
97
- delete (containers , id )
98
- }
99
- if ! aborting {
100
- p .consumer .Status (container , fmt .Sprintf ("exited with code %d" , event .ExitCode ))
101
- if event .Type == api .ContainerEventRecreated {
102
- p .consumer .Status (container , "has been recreated" )
90
+ for {
91
+ select {
92
+ case <- p .stopCh :
93
+ return exitCode , nil
94
+ case event := <- p .queue :
95
+ container , id := event .Container , event .ID
96
+ switch event .Type {
97
+ case api .UserCancel :
98
+ aborting = true
99
+ case api .ContainerEventAttach :
100
+ if _ , ok := containers [id ]; ok {
101
+ continue
102
+ }
103
+ containers [id ] = struct {}{}
104
+ p .consumer .Register (container )
105
+ case api .ContainerEventExit , api .ContainerEventStopped , api .ContainerEventRecreated :
106
+ if ! event .Restarting {
107
+ delete (containers , id )
103
108
}
104
- }
105
- if cascadeStop {
106
109
if ! aborting {
107
- aborting = true
108
- err := stopFn ()
109
- if err != nil {
110
- return 0 , err
110
+ p .consumer .Status (container , fmt .Sprintf ("exited with code %d" , event .ExitCode ))
111
+ if event .Type == api .ContainerEventRecreated {
112
+ p .consumer .Status (container , "has been recreated" )
111
113
}
112
114
}
113
- if event .Type == api .ContainerEventExit {
114
- if exitCodeFrom == "" {
115
- exitCodeFrom = event .Service
115
+ if cascadeStop {
116
+ if ! aborting {
117
+ aborting = true
118
+ err := stopFn ()
119
+ if err != nil {
120
+ return 0 , err
121
+ }
116
122
}
117
- if exitCodeFrom == event .Service {
118
- exitCode = event .ExitCode
123
+ if event .Type == api .ContainerEventExit {
124
+ if exitCodeFrom == "" {
125
+ exitCodeFrom = event .Service
126
+ }
127
+ if exitCodeFrom == event .Service {
128
+ exitCode = event .ExitCode
129
+ }
119
130
}
120
131
}
121
- }
122
- if len ( containers ) == 0 {
123
- // Last container terminated, done
124
- return exitCode , nil
125
- }
126
- case api . ContainerEventLog :
127
- if ! aborting {
128
- p . consumer . Log ( container , event . Line )
129
- }
130
- case api . ContainerEventErr :
131
- if ! aborting {
132
- p . consumer . Err ( container , event . Line )
132
+ if len ( containers ) == 0 {
133
+ // Last container terminated, done
134
+ return exitCode , nil
135
+ }
136
+ case api . ContainerEventLog :
137
+ if ! aborting {
138
+ p . consumer . Log ( container , event . Line )
139
+ }
140
+ case api . ContainerEventErr :
141
+ if ! aborting {
142
+ p . consumer . Err ( container , event . Line )
143
+ }
133
144
}
134
145
}
135
146
}
136
- return exitCode , nil
137
147
}
0 commit comments