Delay the initialization of the thread model until after
backend->config_complete; this is safe, since we don't make any lock
calls until backend->connect. Then add a function callback that
filters can use to further reduce things based on runtime
configuration.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
Is it worth having filters both #define THREAD_MODEL and optionally
declare .thread_model? We have no API stability requirements; we could
instead declare that NBDKIT_REGISTER_FILTER() does NOT require a macro
THREAD_MODEL, and that .thread_model is optional (defaulting to
parallel, but MUST be provided by filters that want to reduce things).
---
docs/nbdkit-filter.pod | 25 ++++++++++++++++++++++---
include/nbdkit-filter.h | 1 +
server/filters.c | 8 ++++++++
server/main.c | 10 ++++++----
4 files changed, 37 insertions(+), 7 deletions(-)
diff --git a/docs/nbdkit-filter.pod b/docs/nbdkit-filter.pod
index 857f241..4033789 100644
--- a/docs/nbdkit-filter.pod
+++ b/docs/nbdkit-filter.pod
@@ -109,12 +109,16 @@ All filters should start by including this header file.
=head1 C<#define THREAD_MODEL>
-All filters must define a thread model. See
-L<nbdkit-plugin(3)/THREADS> for a discussion of thread models.
+All filters must define a default thread model by defining
+C<THREAD_MODEL>. See L<nbdkit-plugin(3)/THREADS> for a discussion of
+thread models.
The final thread model is the smallest (ie. most serialized) out of
all the filters and the plugin. Filters cannot alter the thread model
-to make it larger (more parallel).
+to make it larger (more parallel). However, they can dynamically
+request a smaller (more serialized) model from decisions made during
+C<.config> and C<.config_complete>, by implementing a <.thread_model>
+function callback.
If possible filters should be be written to handle fully parallel
requests (C<NBDKIT_THREAD_MODEL_PARALLEL>, even multiple requests
@@ -267,6 +271,21 @@ what already appears in C<.description>.
If the filter doesn't take any config parameters you should probably
omit this.
+=head2 C<.thread_model>
+
+ int (*thread_model) (void);
+
+This optional function is called after C<.config_complete> to provide
+the filter an opportunity to reduce the resulting thread model below
+the value used at compilation via C<THREAD_MODEL>. The resulting
+thread model for all connections is determined by selecting the most
+restrictive model from the C<THREAD_MODEL> of the plugin and all
+filters, and from the results of any C<.thread_model> across all
+filters.
+
+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) (nbdkit_next_open *next, void *nxdata,
diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h
index 5893dd8..70d32ce 100644
--- a/include/nbdkit-filter.h
+++ b/include/nbdkit-filter.h
@@ -115,6 +115,7 @@ struct nbdkit_filter {
const char *key, const char *value);
int (*config_complete) (nbdkit_next_config_complete *next, void *nxdata);
const char *config_help;
+ int (*thread_model) (void);
void * (*open) (nbdkit_next_open *next, void *nxdata,
int readonly);
diff --git a/server/filters.c b/server/filters.c
index 3bd91fe..87a9c0e 100644
--- a/server/filters.c
+++ b/server/filters.c
@@ -99,6 +99,14 @@ filter_thread_model (struct backend *b)
if (filter_thread_model < thread_model) /* more serialized */
thread_model = filter_thread_model;
+ if (f->filter.thread_model) {
+ filter_thread_model = f->filter.thread_model ();
+ if (filter_thread_model == -1)
+ exit (EXIT_FAILURE);
+ if (filter_thread_model < thread_model)
+ thread_model = filter_thread_model;
+ }
+
return thread_model;
}
diff --git a/server/main.c b/server/main.c
index 8ad3f7a..d46888a 100644
--- a/server/main.c
+++ b/server/main.c
@@ -1,5 +1,5 @@
/* nbdkit
- * Copyright (C) 2013-2018 Red Hat Inc.
+ * Copyright (C) 2013-2019 Red Hat Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -568,9 +568,6 @@ main (int argc, char *argv[])
debug_flags = next;
}
- /* Select a thread model. */
- lock_init_thread_model ();
-
if (help) {
struct backend *b;
@@ -643,6 +640,11 @@ main (int argc, char *argv[])
backend->config_complete (backend);
+ /* Select a thread model. Plugin models are fixed at compile time,
+ * but filters are allowed to reduce the level during config.
+ */
+ lock_init_thread_model ();
+
start_serving ();
backend->free (backend);
--
2.20.1