@@ -129,7 +129,7 @@ func (l *LibVirt) Connect() error {
129129 )
130130
131131 // Start the event loop
132- go l .runEventLoop (context .Background ())
132+ go l .runEventLoop (context .Background (), l . virt )
133133
134134 return nil
135135}
@@ -141,9 +141,13 @@ func (l *LibVirt) Close() error {
141141 return l .virt .Disconnect ()
142142}
143143
144+ // We use this interface in our event loop to detect when the libvirt
145+ // connection has been closed. As an interface, it is easy to mock for testing.
146+ type eventloopRunnable interface { Disconnected () <- chan struct {} }
147+
144148// Run a loop which listens for new events on the subscribed libvirt event
145149// channels and distributes them to the subscribed listeners.
146- func (l * LibVirt ) runEventLoop (ctx context.Context ) {
150+ func (l * LibVirt ) runEventLoop (ctx context.Context , i eventloopRunnable ) {
147151 log := logger .FromContext (ctx , "libvirt" , "event-loop" )
148152 for {
149153 // The reflect.Select function works the same way as a
@@ -161,14 +165,23 @@ func (l *LibVirt) runEventLoop(ctx context.Context) {
161165 }
162166 l .domEventChsLock .Unlock ()
163167
168+ // Add a case to handle context cancellation.
164169 cases = append (cases , reflect.SelectCase {
165170 Dir : reflect .SelectRecv ,
166171 Chan : reflect .ValueOf (ctx .Done ()),
167172 })
168173 caseCtxDone := len (cases ) - 1
169174
175+ // The libvirt connection should never disconnect. If it does,
176+ // we can use the Disconnected channel to detect this.
177+ cases = append (cases , reflect.SelectCase {
178+ Dir : reflect .SelectRecv ,
179+ Chan : reflect .ValueOf (i .Disconnected ()),
180+ })
181+ caseLibvirtDisconnected := len (cases ) - 1
182+
170183 chosen , value , ok := reflect .Select (cases )
171- if ! ok {
184+ if ! ok || chosen == caseLibvirtDisconnected {
172185 // This should never happen. If it does, give the
173186 // service a chance to restart and reconnect.
174187 panic ("libvirt connection closed" )
0 commit comments