Skip to content

Commit eb38ab6

Browse files
committed
qemu/block: fixes deadlock in waitForSignal
previously, waitForSignal executed a command prior to listening on the event stream. If an event arrived prior to the commands return, the event stream would block forever.
1 parent d5e8dbf commit eb38ab6

File tree

1 file changed

+11
-7
lines changed

1 file changed

+11
-7
lines changed

qemu/block.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -225,21 +225,25 @@ func (bd BlockDevice) Snapshot(d *Domain, overlay string) error {
225225
// waitForSignal opens a domain's QMP event stream and invokes an input
226226
// closure to send commands which would create results on that event stream.
227227
func waitForSignal(d *Domain, signal string, timeout time.Duration, fn func() error) error {
228-
// "done" signal must be sent in both the error and non-error case, to
229-
// avoid leaking goroutines.
228+
// "done" signal must always be sent to avoid leaking goroutines.
230229
events, done, err := d.Events()
231230
if err != nil {
232231
return err
233232
}
233+
defer func() { done <- struct{}{} }()
234234

235-
if err = fn(); err != nil {
236-
done <- struct{}{}
235+
// start listening for events prior to command execution. QMP events
236+
// may emit before the command returns.
237+
jobErr := make(chan error)
238+
go func() {
239+
jobErr <- waitForJob(events, signal, timeout)
240+
}()
241+
242+
if err := fn(); err != nil {
237243
return err
238244
}
239245

240-
err = waitForJob(events, signal, timeout)
241-
done <- struct{}{}
242-
return err
246+
return <-jobErr
243247
}
244248

245249
// waitForJob monitors the domain's QMP event stream, waiting

0 commit comments

Comments
 (0)