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