[Libguestfs] [nbdkit PATCH 1/2] plugins: Add .thread_model callback

Eric Blake
2019-05-20T12:30:31Z

Plugins cannot get rid of #define THREAD_MODEL (doing so would break
API/ABI compatibility); however, we CAN add an optional callback for
allowing a runtime reduction of the thread model. This can be
especially useful for language bindings, as in the next patch, where
the C glue code for the language has to cater to the maximum
parallelism possible for that language, but where an individual plugin
may still want to be stricter.

Signed-off-by: Eric Blake <eblake@redhat.com>
---
 docs/nbdkit-plugin.pod  | 33 +++++++++++++++++++++++++++++----
 include/nbdkit-plugin.h |  2 ++
 server/plugins.c        | 12 +++++++++++-
 3 files changed, 42 insertions(+), 5 deletions(-)

diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 7f83234..9a20663 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -133,6 +133,12 @@ has been passed to the plugin.
 Both are called after loading the plugin but before any connections
 are accepted.

+=item C<.thread_model>
+
+C<.thread_model> is called once after all configuration information
+has been passed to the plugin, and before any connections are
+accepted.
+
 =item C<.open>

 A new client has connected.
@@ -466,6 +472,19 @@ what already appears in C<.description>.
 If the plugin doesn't take any config parameters you should probably
 omit this.

+=head2 C<.thread_model>
+
+ int thread_model (void)
+
+This optional callback is called after all the configuration has been
+passed to the plugin.  It can be used to force a stricter thread model
+based on configuration, compared to C<THREAD_MODEL>.  See
L</THREADS>
+below for details.  Attempts to request a looser (more parallel) model
+are silently ignored.
+
+If there is an error, C<.thread_model> should call C<nbdkit_error>
+with an error message and return C<-1>.
+
 =head2 C<.open>

  void *open (int readonly);
@@ -898,9 +917,14 @@ error message, and C<nbdkit_set_error> to record an
appropriate error

 =head1 THREADS

-Each nbdkit plugin must declare its thread safety model by defining
-the C<THREAD_MODEL> macro.  (This macro is used by
-C<NBDKIT_REGISTER_PLUGIN>).
+Each nbdkit plugin must declare its maximum thread safety model by
+defining the C<THREAD_MODEL> macro.  (This macro is used by
+C<NBDKIT_REGISTER_PLUGIN>).  Additionally, a plugin may implement the
+C<.thread_model> callback, called right after C<.config_complete>
to
+make a runtime decision on which thread model to use.  The nbdkit
+server chooses the most restrictive model between the plugin's
+C<THREAD_MODEL>, the C<.thread_model> if present, and any
restrictions
+requested by filters.

 The possible settings for C<THREAD_MODEL> are defined below.

@@ -935,7 +959,8 @@ parallel.  However only one request will happen per handle
at a time
 =item C<#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL>

 Multiple handles can be open and multiple data requests can happen in
-parallel (even on the same handle).
+parallel (even on the same handle).  The server may reorder replies,
+answering a later request before an earlier one.

 All the libraries you use must be thread-safe and reentrant.  You may
 also need to provide mutexes for fields in your connection handle.
diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index e9b1808..632df86 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -130,6 +130,8 @@ struct nbdkit_plugin {
                   struct nbdkit_extents *extents);
   int (*can_cache) (void *handle);
   int (*cache) (void *handle, uint32_t count, uint64_t offset, uint32_t flags);
+
+  int (*thread_model) (void);
 };

 extern void nbdkit_set_error (int err);
diff --git a/server/plugins.c b/server/plugins.c
index f293e6a..85736bb 100644
--- a/server/plugins.c
+++ b/server/plugins.c
@@ -87,8 +87,18 @@ static int
 plugin_thread_model (struct backend *b)
 {
   struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
+  int thread_model = p->plugin._thread_model;
+  int r;

-  return p->plugin._thread_model;
+  if (p->plugin.thread_model) {
+    r = p->plugin.thread_model ();
+    if (r == -1)
+      exit (EXIT_FAILURE);
+    if (r < thread_model)
+      thread_model = r;
+  }
+
+  return thread_model;
 }

 static const char *
--
2.20.1