Skip to content
This repository has been archived by the owner on Jul 16, 2020. It is now read-only.

Commit

Permalink
qemu: Document how cancelling works.
Browse files Browse the repository at this point in the history
The code that handles the serialization and cancelling of QMP commands
is a little complex and it took me some time to remember how it actually
works and why it works in this particular way.  For this reason I've
added some comments which will hopefully make the next bug fix in this
area a little less painful.

Signed-off-by: Mark Ryan <[email protected]>
  • Loading branch information
Mark Ryan committed Feb 16, 2017
1 parent 636cdee commit 0d829ab
Showing 1 changed file with 33 additions and 0 deletions.
33 changes: 33 additions & 0 deletions qemu/qmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,39 @@ func (q *QMP) parseVersion(version []byte) *QMPVersion {
}
}

// The qemu package allows multiple QMP commands to be submitted concurrently
// from different Go routines. Unfortunately, QMP doesn't really support parallel
// commands as there is no way reliable way to associate a command response
// with a request. For this reason we need to submit our commands to
// QMP serially. The qemu package performs this serialisation using a
// queue (cmdQueue owned by mainLoop). We use a queue rather than a simple
// mutex so we can support cancelling of commands (see below) and ordered
// execution of commands, i.e., if command B is issued before command C,
// it should be executed before command C even if both commands are initially
// blocked waiting for command A to finish. This would be hard to achieve with
// a simple mutex.
//
// Cancelling is a little tricky. Commands such as ExecuteQMPCapabilities
// can be cancelled by cancelling or timing out their contexts. When a
// command is cancelled the calling function, e.g., ExecuteQMPCapabilities,
// will return but we may not be able to remove the command's entry from
// the command queue or issue the next command. There are two scenarios
// here.
//
// 1. The command has been processed by QMP, i.e., we have received a
// return or an error, but is still blocking as it is waiting for
// an event. For example, the ExecuteDeviceDel blocks until a DEVICE_DELETED
// event is received. When such a command is cancelled we can remove it
// from the queue and start issuing the next command. When the DEVICE_DELETED
// event eventually arrives it will just be ignored.
//
// 2. The command has not been processed by QMP. In this case the command
// needs to remain on the cmdQueue until the response to this command is
// received from QMP. During this time no new commands can be issued. When the
// response is received, it is discarded (as no one is interested in the result
// any more), the entry is removed from the cmdQueue and we can proceed to
// execute the next command.

func (q *QMP) mainLoop() {
cmdQueue := list.New().Init()
fromVMCh := make(chan []byte)
Expand Down

0 comments on commit 0d829ab

Please sign in to comment.