On 28/08/10 13:16, Richard W.M. Jones wrote:
From 978997bb41015eb85f009d4d48ca1716c84d9a50 Mon Sep 17 00:00:00 2001 From: Richard Jones<rjones@redhat.com> Date: Sat, 28 Aug 2010 10:33:24 +0100 Subject: [PATCH 1/4] Implement progress messages in the daemon and library.
This implements progress notification messages in the daemon, and adds a callback in the library to handle them.
No calls are changed so far, so in fact no progress messages can be generated by this commit.
For more details, see: https://www.redhat.com/archives/libguestfs/2010-July/msg00003.html https://www.redhat.com/archives/libguestfs/2010-July/msg00024.html --- daemon/daemon.h | 7 ++++ daemon/proto.c | 88 +++++++++++++++++++++++++++++++++++++++++++++--- src/generator.ml | 21 +++++++++++- src/guestfs-internal.h | 2 + src/guestfs.c | 8 ++++ src/guestfs.h | 5 ++- src/guestfs.pod | 57 +++++++++++++++++++++++++++++++ src/proto.c | 37 ++++++++++++++++++-- 8 files changed, 215 insertions(+), 10 deletions(-)
--- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -1186,6 +1186,63 @@ languages (eg. if your HLL interpreter has already been cleaned up by the time this is called, and if your callback then jumps into some HLL function).
+=head2 guestfs_set_progress_callback + + typedef void (*guestfs_progress_cb) (guestfs_h *g, void *opaque, + int proc_nr, int serial, + uint64_t position, uint64_t total); + void guestfs_set_progress_callback (guestfs_h *g, + guestfs_progress_cb cb, + void *opaque); + +Some long-running operations can generate progress messages. If +this callback is registered, then it will be called each time a +progress message is generated (usually one second after the +operation started, and every second thereafter until it completes, +although the frequency may change in future versions). + +The callback receives two numbers: C<position> and C<total>. +The units of C<total> are not defined, although for some +operations C<total> may relate in some way to the amount of +data to be transferred (eg. in bytes or megabytes), and +C<position> may be the proportion which has been transferred.
s/proportion/(portion|amount|total)/
+The only defined and stable parts of the API are: + +=over 4 + +=item * + +The callback can display to the user some type of progress bar or +indicator which shows the ratio of C<position>:C<total>. + +=item * + +C<total> will be the same number for each progress message within +a single operation.
Is this restriction truly necessary? This api would be useful and well-defined with just the first and third restrictions. Allowing total to change over time is useful for estimated durations.
+=item * + +0 E<lt>= C<position> E<lt>= C<total> + +=item * + +If any progress notification is sent during a call, then a final +progress notification is always sent when C<position> = C<total>. + +This is to simplify caller code, so callers can easily set the +progress indicator to "100%" at the end of the operation, without +requiring special code to detect this case. + +=back + +The callback also receives the procedure number and serial number of +the call. These are only useful for debugging protocol issues, and +the callback can normally ignore them. The callback may want to +print these numbers in error messages or debugging messages. + +Progress callbacks only happen in the BUSY state. + =head1 BLOCK DEVICE NAMING
In the kernel there is now quite a profusion of schemata for naming diff --git a/src/proto.c b/src/proto.c index ad173c6..f9b5a33 100644 --- a/src/proto.c +++ b/src/proto.c @@ -373,17 +373,26 @@ guestfs___send_to_daemon (guestfs_h *g, const void *v_buf, size_t n) * * It also checks for EOF (qemu died) and passes that up through the * child_cleanup function above. + * + * Progress notifications are handled transparently by this function. + * If the callback exists, it is called. The caller of this function + * will not see GUESTFS_PROGRESS_FLAG. */ int guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) { fd_set rset, rset2;
+#define PROGRESS_MESSAGE_SIZE 24
We really should be calculating this value automatically somehow. Is there any structure we can measure the size of?
+#define message_size() \ + (*size_rtn != GUESTFS_PROGRESS_FLAG ? *size_rtn : PROGRESS_MESSAGE_SIZE) + if (g->verbose) fprintf (stderr, "recv_from_daemon: %p g->state = %d, size_rtn = %p, buf_rtn = %p\n", g, g->state, size_rtn, buf_rtn);
+ again: FD_ZERO (&rset);
FD_SET (g->fd[1],&rset); /* Read qemu stdout for log messages& EOF. */ @@ -400,7 +409,7 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) */ ssize_t nr = -4;
- while (nr< (ssize_t) *size_rtn) { + while (nr< (ssize_t) message_size()) {
That loop test is way too complex. This could be rewritten as: for (;;) { ssize_t message_size = *size_rtn != GUESTFS_PROGRESS_FLAG ? *size_rtn : PROGRESS_MESSAGE_SIZE; if (nr < message_size) break; This would also avoid the unsightly #define, be a bit nicer to the compiler, and re-use the value below.
rset2 = rset; int r = select (max_fd+1,&rset2, NULL, NULL, NULL); if (r == -1) { @@ -463,6 +472,8 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) } else if (*size_rtn == GUESTFS_CANCEL_FLAG) return 0; + else if (*size_rtn == GUESTFS_PROGRESS_FLAG) + /*FALLTHROUGH*/; /* If this happens, it's pretty bad and we've probably lost * synchronization. */ @@ -473,11 +484,11 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) }
/* Allocate the complete buffer, size now known. */ - *buf_rtn = safe_malloc (g, *size_rtn); + *buf_rtn = safe_malloc (g, message_size()); /*FALLTHROUGH*/ }
- size_t sizetoread = *size_rtn - nr; + size_t sizetoread = message_size() - nr; if (sizetoread> BUFSIZ) sizetoread = BUFSIZ;
r = read (g->sock, (char *) (*buf_rtn) + nr, sizetoread); @@ -524,6 +535,26 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) } #endif
+ if (*size_rtn == GUESTFS_PROGRESS_FLAG) { + if (g->state == BUSY&& g->progress_cb) { + guestfs_progress message; + XDR xdr; + xdrmem_create (&xdr, *buf_rtn, PROGRESS_MESSAGE_SIZE, XDR_DECODE); + xdr_guestfs_progress (&xdr,&message); + xdr_destroy (&xdr); + + g->progress_cb (g, g->progress_cb_data, + message.proc, message.serial, + message.position, message.total); + } + + free (*buf_rtn); + *buf_rtn = NULL; + + /* Process next message. */ + goto again;
This is nasty. You could just return guestfs___recv_from_daemon, called recursively. I think gcc is pretty efficient with the stack in this case.
+ } + return 0; }
-- 1.7.1
-- Matthew Booth, RHCA, RHCSS Red Hat Engineering, Virtualisation Team GPG ID: D33C3490 GPG FPR: 3733 612D 2D05 5458 8A8A 1600 3441 EA19 D33C 3490