A generic client exploiting multiple in-flight commands should be
prepared for out-of-order responses (and should probably ensure that
there are no offset/count overlaps between parallel in-flight commands
to avoid unspecified disk contents if the server acts on commands in
an arbitrary order or even exposing non-atomic splicing effects). But
a specific client aware of a specific server's behavior may have
reason to depend on fully serialized answers. For example, knowing
whether a write response arrived prior to a flush response can be used
to learn whether the flush covered the write, or whether another flush
may be needed. We need both a way to let the client query which
command completed first (a new nbd_aio_peek_command_completed) and to
treat the completed list as a queue rather than a stack to preserve
FIFO order.
---
generator/generator | 12 ++++++++++++
generator/states-reply.c | 13 ++++++++++---
lib/aio.c | 13 +++++++++++++
3 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/generator/generator b/generator/generator
index 60dfb72..a1523e1 100755
--- a/generator/generator
+++ b/generator/generator
@@ -1628,6 +1628,18 @@ The C<handle> parameter is the unique 64 bit handle for the
command,
as returned by a call such as C<nbd_aio_pread>.";
};
+ "aio_peek_command_completed", {
+ default_call with
+ args = []; ret = RInt64;
+ shortdesc = "check if any command has completed";
+ longdesc = "\
+Return the unique 64 bit handle of the first non-retired but completed
+command, or -1 if no command is awaiting retirement. Any handle
+returned by this function must still be passed to
+C<aio_command_completed> to actually retire the command and learn
+whether the command was successful.";
+ };
+
"connection_state", {
default_call with
args = []; ret = RConstString;
diff --git a/generator/states-reply.c b/generator/states-reply.c
index 93f6cda..45362d4 100644
--- a/generator/states-reply.c
+++ b/generator/states-reply.c
@@ -103,13 +103,20 @@
}
assert (cmd != NULL);
- /* Move it to the cmds_done list. */
+ /* Move it to the end of the cmds_done list. */
if (prev_cmd != NULL)
prev_cmd->next = cmd->next;
else
conn->cmds_in_flight = cmd->next;
- cmd->next = conn->cmds_done;
- conn->cmds_done = cmd;
+ cmd->next = NULL;
+ if (conn->cmds_done) {
+ prev_cmd = conn->cmds_done;
+ while (prev_cmd->next)
+ prev_cmd = prev_cmd->next;
+ prev_cmd->next = cmd;
+ }
+ else
+ conn->cmds_done = cmd;
SET_NEXT_STATE (%.READY);
return 0;
diff --git a/lib/aio.c b/lib/aio.c
index c7764f8..105651f 100644
--- a/lib/aio.c
+++ b/lib/aio.c
@@ -158,3 +158,16 @@ nbd_unlocked_aio_command_completed (struct nbd_connection *conn,
nbd_internal_name_of_nbd_cmd (type), strerror (error));
return -1;
}
+
+int64_t
+nbd_unlocked_aio_peek_command_completed (struct nbd_connection *conn)
+{
+ if (conn->cmds_done != NULL)
+ return conn->cmds_done->handle;
+
+ if (conn->cmds_in_flight != NULL || conn->cmds_to_issue != NULL)
+ set_error (0, "no in-flight command has completed yet");
+ else
+ set_error (0, "no commands are in flight");
+ return -1;
+}
--
2.20.1