[Libguestfs] [PATCH nbdkit 1/2] server: Add .after_fork callback, mainly for plugins to create threads.
Richard W.M. Jones
rjones at redhat.com
Mon Jun 22 20:15:08 UTC 2020
On Mon, Jun 22, 2020 at 02:00:50PM -0500, Eric Blake wrote:
> >+++ b/docs/nbdkit-filter.pod
>
> >+=head2 C<.after_fork>
> >+
> >+ int (*after_fork) (nbdkit_next_after_fork *next, void *nxdata);
> >+
> >+This intercepts the plugin C<.after_fork> method and can be used by
> >+the filter to start background threads (although these have limited
> >+usefulness since they will not be able to access the plugin).
> >+
> >+If there is an error, C<.after_fork> should call C<nbdkit_error> with
> >+an error message and return C<-1>.
>
> Should the backend code guarantee that .after_fork is called for all
> layers in the backend chain? The only flexibility I see us gaining
> by letting the filter choose when .after_fork is called is
> fine-grained control over what threads the filter can start before
> or after the plugin's .after_fork, but is that flexibility really
> needed, or are we equally safe if the backend just always calls
> .after_fork and the filter doesn't have to worry about calling
> next(nxdata)?
>
> Of course, given that filters are in-tree, we can change this
> signature as needed later on, even if we make it void for now we can
> hook it into passing .after_fork later if we find a use for that
> finer-grained call semantics.
In theory a filter that wanted background threads would have exactly
the same problem as a plugin and would need to add an .after_fork
callback to create them. I can't think of a situation where a filter
would be correct to not call the plugin's .after_fork, but I suppose
it's plausible it might want to do something before or after the
plugin.
The biggest practical problem at the moment however is ...
At this point I would link to the TODO file on github, but it seems
like github is down. If you look at the TODO file you'll see I
mentioned a general problem with background threads in filters.
Anyway as you say they're all in tree so we can worry about this
later.
> >+=item C<.after_fork>
> >+
> >+In normal operation, C<.after_fork> is called after the server has
> >+forked into the background and changed UID and directory. If a plugin
> >+needs to create background threads (or uses an external library that
> >+creates threads) it should do so here, because background threads are
> >+killed by fork.
>
> s/killed/invalidated/ (they are still alive in the parent process,
> but after the fork we are no longer in the parent process to
> interact with them)
OK, I'll change this. It's also of course a point that if a plugin
creates background threads before the fork and they grab locks then
that could lead to problems. So I need to add a note to .get_ready
saying that you shouldn't create background threads there.
> >+
> >+Because the server may have forked into the background, error messages
> >+and failures from C<.after_fork> cannot be seen by the user unless
> >+they look through syslog. An error in C<.after_fork> can appear to
> >+the user as if nbdkit “just died”. So in almost all cases it is
> >+better to use C<.get_ready> instead of this callback, or to do as much
> >+preparation work as possible in C<.get_ready> and only start
> >+background threads here.
> >+
> >+The server doesn't always fork (eg. if the I<-f> flag is used), but
> >+even so this callback will be called. If you want to find out if the
> >+server forked between C<.get_ready> and C<.after_fork> use
> >+L<getpid(2)>.
>
> Thinking about back-compat:
> We promise that plugins compiled against older nbdkit will continue
> to run against newer nbdkit, and that is preserved here.
> What we do not promise is that plugins compiled against newer nbdkit
> will work wehn loaded by older nbdkit. This is one of those cases:
> anything you stick in .after_fork will not be called by older nbdkit
> which didn't know about the callback.
>
> At present, we silently ignore the tail of 'struct plugin' from any
> plugin compiled against newer nbdkit when loaded by older nbdkit.
> Should we tweak that to instead output a warning that the plugin
> might rely on features present only in newer nbdkit and therefore
> might not work in the current nbdkit version? Of course, that
> doesn't help the fact that existing older nbdkit is lacking this
> safety check.
It should probably only do that if the tail of the struct is non-zero,
because who cares if it contains NULL pointers. But that's another
patch.
> Do we need to instead add some sort of API for plugins to be able to
> learn which features are provided by the nbdkit loading the plugin,
> so that a plugin that requires the use of 1.22 nbdkit features (such
> as .after_fork) can gracefully fail during .config_complete (or
> similar) when loaded by a 1.20 nbdkit, rather than mysteriously
> failing to work when .after_fork is not called? [In a distro
> setting, the solution is version dependencies: you could make it so
> that installing the nbdkit-vddk-plugin package forces an upgrade of
> the nbdkit package to 1.22 - but this is more a question for
> situations not covered by distro packaging setups]
TBH this is a non-supported case so if it fails you get to keep all
the pieces.
> >+=head2 C<.after_fork>
> >+
> >+ int after_ready (void);
>
> Typo in the function name.
Oops.
> >+++ b/server/filters.c
> >@@ -193,6 +193,28 @@ filter_get_ready (struct backend *b)
> > b->next->get_ready (b->next);
> > }
> >+static int
> >+next_after_fork (struct backend *b)
> >+{
> >+ b->after_fork (b);
> >+ return 0;
> >+}
> >+
> >+static void
> >+filter_after_fork (struct backend *b)
> >+{
> >+ struct backend_filter *f = container_of (b, struct backend_filter, backend);
> >+
> >+ debug ("%s: after_fork", b->name);
> >+
> >+ if (f->filter.after_fork) {
> >+ if (f->filter.after_fork (next_after_fork, b->next) == -1)
> >+ exit (EXIT_FAILURE);
> >+ }
> >+ else
> >+ b->next->after_fork (b->next);
> >+}
>
> This code changes if we decide that filters could live with 'void'
> instead of next_after_fork, because we mandate the chain traversal
> through the backend rather than leaving it up to the filters.
>
> Still a few tweaks needed, but the idea looks sane.
Rich.
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-df lists disk usage of guests without needing to install any
software inside the virtual machine. Supports Linux and Windows.
http://people.redhat.com/~rjones/virt-df/
More information about the Libguestfs
mailing list