Re: [PATCH nbdkit] Remove nbdkit-ruby-plugin and Ruby language support completely
by Eric Blake
On Wed, Apr 17, 2024 at 12:42:42PM +0100, Daniel P. Berrangé wrote:
> > This strategy fails completely if you have threads. In a thread
> > (using a different stack entirely) it scans randomly across the heap,
> > resulting in weird behaviour at best, but more usually straight
> > crashes.
>
> Urgh, horrible.
>
> >
> > We've had a Ruby plugin for a long time, but it broke with Ruby 1.9,
> > when Ruby changed from some kind of cooperative threading to using
> > native threads (and therefore separate stacks).
> >
> > The tests have been disabled since early 2018 (nbdkit < 1.2). I
> > re-enabled the tests just now (Ruby 3.3) but it's broken in the same
> > way as ever.
> >
> > The only changes that have happened since the tests were disabled are
> > mechanical ones / project-wide cleanups.
> >
> > It currently crashes in open().
> >
> > It seems highly unlikely that anyone is using this plugin.
>
> IIUC, the '--threads' arg is just a per-client limit. So if
> there are multiple clients, you'll still get many threads
> using ruby even with "--threads = 1".
If the plugin requests NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS, then
--threads is ignored (see server/connections.c that forces nworkers to
0 if thread_model < NBDKIT_THREAD_MODEL_PARALLEL). But the Ruby
plugin was already requesting
NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS, and it is still broken, so
this isn't helping.
>
> If spawning nbdkit with --single though, you'll get a separate
> process per client, and then with --threads=1, you could can
> sure that only 1 thread is calling into ruby.
>
> Is there a way a plugin can declare that it is non-threadsafe,
> and thus allow nbdkit to refuse to run it without --single
> and --threads=1, being set ?
The thread_model interface should be usable for this, if we thought it
was worth trying to keep Ruby alive in this limited use scenario.
>
> I guess that will complicate your test suite though unless
> you can skip all tests which rely on multi-connections and
> threads.
Any test for multi-conn across all compiled plugins/filters should
already be checking --help output to ensure whether the plugin
supports parallel behavior and skip if not supported (if we decide
that the Ruby plugin should advertise that it must run non-threaded,
rather than just dropping it).
--
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization: qemu.org | libguestfs.org
7 months, 1 week
[PATCH nbdkit] Remove nbdkit-ruby-plugin and Ruby language support completely
by Richard W.M. Jones
This has not worked for a long time because of a fundamental bug in
how Ruby's garbage collector interacts with pthread stacks. There has
been a bug open for a long time (over 14 years) which has never been
fixed:
https://redmine.ruby-lang.org/issues/2294#note-34
As that discussion is rather hard to follow, the basic problem is
this:
When you embed Ruby you must tell the interpreter where "the stack"
is. You do this by calling RUBY_INIT_STACK early on (which we do in
load(), on the main thread). This marks the top of stack.
From time to time Ruby's garbage collector will scan the stack
starting at the current stack pointer, and going up til it reaches the
previously marked top of stack. It scans this memory looking for
roots etc.
This strategy fails completely if you have threads. In a thread
(using a different stack entirely) it scans randomly across the heap,
resulting in weird behaviour at best, but more usually straight
crashes.
We've had a Ruby plugin for a long time, but it broke with Ruby 1.9,
when Ruby changed from some kind of cooperative threading to using
native threads (and therefore separate stacks).
The tests have been disabled since early 2018 (nbdkit < 1.2). I
re-enabled the tests just now (Ruby 3.3) but it's broken in the same
way as ever.
The only changes that have happened since the tests were disabled are
mechanical ones / project-wide cleanups.
It currently crashes in open().
It seems highly unlikely that anyone is using this plugin.
Remove the plugin for nbdkit 1.40.
---
docs/nbdkit-plugin.pod | 6 +-
plugins/ruby/nbdkit-ruby-plugin.pod | 318 ----------------
configure.ac | 21 --
plugins/ruby/Makefile.am | 83 -----
tests/Makefile.am | 28 --
plugins/ruby/ruby.c | 549 ----------------------------
plugins/ruby/example.rb | 62 ----
tests/test-dump-plugin.sh | 2 +-
tests/test-help-plugin.sh | 2 +-
tests/test-no-parameters.sh | 3 +-
tests/test-plugin-docs.sh | 2 +-
tests/test-shebang-ruby.sh | 78 ----
tests/test-version-plugin.sh | 2 +-
tests/shebang.rb | 18 -
tests/test-lang-plugins.c | 4 +-
.gitignore | 1 -
README.md | 2 +-
ci/build.sh | 1 -
18 files changed, 11 insertions(+), 1171 deletions(-)
diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 07693824de..6bcf85d997 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -1579,8 +1579,10 @@ define the C<nbdkit_plugin> struct is slightly different:
=head1 WRITING PLUGINS IN OTHER PROGRAMMING LANGUAGES
You can also write nbdkit plugins in Go, Lua, OCaml, Perl, Python,
-Ruby, Rust, shell script or Tcl. Other programming languages may be
-offered in future.
+Rust, shell script or Tcl. Other programming languages may be offered
+in future.
+
+Ruby plugin support was removed in nbdkit 1.40.
For more information see:
__LANG_PLUGIN_LINKS__.
diff --git a/plugins/ruby/nbdkit-ruby-plugin.pod b/plugins/ruby/nbdkit-ruby-plugin.pod
deleted file mode 100644
index d33f97cfd9..0000000000
--- a/plugins/ruby/nbdkit-ruby-plugin.pod
+++ /dev/null
@@ -1,318 +0,0 @@
-=head1 NAME
-
-nbdkit-ruby-plugin - nbdkit ruby plugin
-
-=head1 SYNOPSIS
-
- nbdkit ruby /path/to/plugin.rb [arguments...]
-
-=head1 WARNING
-
-The Ruby language is fundamentally broken when it comes to embedding
-in a program which uses pthreads. This means you may see random
-"stack overflows" when using this plugin on some versions of Ruby but
-not others.
-
-For the whole sorry saga, see:
-L<https://redmine.ruby-lang.org/issues/2294>
-
-=head1 DESCRIPTION
-
-C<nbdkit-ruby-plugin> is an embedded Ruby interpreter for
-L<nbdkit(1)>, allowing you to write nbdkit plugins in Ruby.
-
-=head2 If you have been given an nbdkit Ruby plugin
-
-Assuming you have a Ruby script which is an nbdkit plugin, you run it
-like this:
-
- nbdkit ruby /path/to/ruby.rb
-
-You may have to add further C<key=value> arguments to the command
-line. Read the Ruby script to see if it requires any.
-
-=head1 WRITING A RUBY NBDKIT PLUGIN
-
-For an example plugin written in Ruby, see:
-L<https://gitlab.com/nbdkit/nbdkit/blob/master/plugins/ruby/example.rb>
-
-Broadly speaking, Ruby nbdkit plugins work like C ones, so you should
-read L<nbdkit-plugin(3)> first.
-
-To write a Ruby nbdkit plugin, you create a Ruby file which
-contains at least the following required functions:
-
- def open(readonly)
- # see below
- end
- def get_size(h)
- # see below
- end
- def pread(h, count, offset)
- # see below
- end
-
-Note that the subroutines must have those literal names (like C<open>),
-because the C part looks up and calls those functions directly. You
-may want to include documentation and globals (eg. for storing global
-state). Any other top level statements are run when the script is
-loaded, just like ordinary Ruby.
-
-=head2 Executable script
-
-If you want you can make the script executable and include a "shebang"
-at the top:
-
- #!/usr/sbin/nbdkit ruby
-
-See also L<nbdkit(1)/Shebang scripts>.
-
-These scripts can also be installed in the C<$plugindir>. See
-L<nbdkit-plugin(3)/WRITING PLUGINS IN OTHER PROGRAMMING LANGUAGES>.
-
-=head2 Methods
-
-Your script has access to the C<Nbdkit> module, with the following
-singleton methods:
-
- Nbdkit.set_error(err)
-
-Record C<err> as the reason you are about to raise an
-exception. C<err> should either be a class that defines an C<Errno>
-constant (all of the subclasses of C<SystemCallError> in module
-C<Errno> have this property), an object that defines an C<errno>
-method with no arguments (all instances of C<SystemCallError> have
-this property), or an integer value corresponding to the usual errno
-values.
-
-=head2 Exceptions
-
-Ruby callbacks should throw exceptions to indicate errors. Remember
-to use C<Nbdkit.set_error> if you need to control which error is sent
-back to the client; if omitted, the client will see an error of C<EIO>.
-
-=head2 Ruby callbacks
-
-This just documents the arguments to the callbacks in Ruby, and any
-way that they differ from the C callbacks. In all other respects they
-work the same way as the C callbacks, so you should go and read
-L<nbdkit-plugin(3)>.
-
-=over 4
-
-=item C<dump_plugin>
-
-(Optional)
-
-There are no arguments or return value.
-
-=item C<config>
-
-(Optional)
-
- def config(key, value)
- # no return value
- end
-
-=item C<config_complete>
-
-(Optional)
-
-There are no arguments or return value.
-
-=item C<open>
-
-(Required)
-
- def open(readonly)
- # return handle
- end
-
-You can return any non-nil Ruby value as the handle. It is passed
-back in subsequent calls.
-
-=item C<close>
-
-(Optional)
-
- def close(h)
- # no return value
- end
-
-=item C<get_size>
-
-(Required)
-
- def get_size(h)
- # return the size of the disk
- end
-
-=item C<can_write>
-
-(Optional)
-
- def can_write(h)
- # return a boolean
- end
-
-=item C<can_flush>
-
-(Optional)
-
- def can_flush(h)
- # return a boolean
- end
-
-=item C<is_rotational>
-
-(Optional)
-
- def is_rotational(h)
- # return a boolean
- end
-
-=item C<can_trim>
-
-(Optional)
-
- def can_trim(h)
- # return a boolean
- end
-
-=item C<pread>
-
-(Required)
-
- def pread(h, count, offset)
- # construct a string of length count bytes and return it
- end
-
-The body of your C<pread> function should construct a string of length
-(at least) C<count> bytes. You should read C<count> bytes from the
-disk starting at C<offset>.
-
-NBD only supports whole reads, so your function should try to read
-the whole region (perhaps requiring a loop). If the read fails or
-is partial, your function should throw an exception, optionally using
-C<Nbdkit.set_error> first.
-
-=item C<pwrite>
-
-(Optional)
-
- def pwrite(h, buf, offset)
- length = buf.length
- # no return value
- end
-
-The body of your C<pwrite> function should write the C<buf> string to
-the disk. You should write C<count> bytes to the disk starting at
-C<offset>.
-
-NBD only supports whole writes, so your function should try to
-write the whole region (perhaps requiring a loop). If the write
-fails or is partial, your function should throw an exception, optionally
-using C<Nbdkit.set_error> first.
-
-=item C<flush>
-
-(Optional)
-
- def flush(h)
- # no return value
- end
-
-The body of your C<flush> function should do a L<sync(2)> or
-L<fdatasync(2)> or equivalent on the backing store.
-
-If the flush fails, your function should throw an exception, optionally
-using C<Nbdkit.set_error> first.
-
-=item C<trim>
-
-(Optional)
-
- def trim(h, count, offset)
- # no return value
- end
-
-The body of your C<trim> function should "punch a hole" in the
-backing store. If the trim fails, your function should throw an
-exception, optionally using C<Nbdkit.set_error> first.
-
-=item C<zero>
-
-(Optional)
-
- def zero(h, count, offset, may_trim)
- # no return value
-
-The body of your C<zero> function should ensure that C<count> bytes
-of the disk, starting at C<offset>, will read back as zero. If
-C<may_trim> is true, the operation may be optimized as a trim as long
-as subsequent reads see zeroes.
-
-NBD only supports whole writes, so your function should try to
-write the whole region (perhaps requiring a loop). If the write
-fails or is partial, your function should throw an exception,
-optionally using C<Nbdkit.set_error> first. In particular, if
-you would like to automatically fall back to C<pwrite> (perhaps
-because there is nothing to optimize if C<may_trim> is false),
-use C<Nbdkit.set_error(Errno::EOPNOTSUPP)>.
-
-=back
-
-=head2 Missing callbacks
-
-=over 4
-
-=item Missing: C<load> and C<unload>
-
-These are not needed because you can just use ordinary Ruby
-constructs.
-
-=item Missing: C<name>, C<version>, C<longname>, C<description>,
-C<config_help>, C<can_fua>, C<can_cache>, C<cache>
-
-These are not yet supported.
-
-=back
-
-=head2 Threads
-
-The thread model for Ruby callbacks currently cannot be set from Ruby.
-It is hard-coded in the C part to
-C<NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS>. This may change or be
-settable in future.
-
-=head1 FILES
-
-=over 4
-
-=item F<$plugindir/nbdkit-ruby-plugin.so>
-
-The plugin.
-
-Use C<nbdkit --dump-config> to find the location of C<$plugindir>.
-
-=back
-
-=head1 VERSION
-
-C<nbdkit-ruby-plugin> first appeared in nbdkit 1.2.
-
-=head1 SEE ALSO
-
-L<nbdkit(1)>,
-L<nbdkit-plugin(3)>,
-L<ruby(1)>.
-
-=head1 AUTHORS
-
-Eric Blake
-
-Richard W.M. Jones
-
-=head1 COPYRIGHT
-
-Copyright Red Hat
diff --git a/configure.ac b/configure.ac
index 3e3afd9ba8..b71c430c6a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -68,7 +68,6 @@ lang_plugins="\
ocaml \
perl \
python \
- ruby \
rust \
sh \
tcl \
@@ -1077,24 +1076,6 @@ AS_IF([test "x$have_rust" = "xyes"], [
printf "rustc version: "; $RUSTC --version
])
-dnl Check for Ruby, for embedding in the Ruby plugin.
-AC_CHECK_PROG([RUBY],[ruby],[ruby],[no])
-AC_ARG_ENABLE([ruby],
- [AS_HELP_STRING([--disable-ruby], [disable Ruby plugin])],
- [],
- [enable_ruby=yes])
-AS_IF([test "x$RUBY" != "xno" && test "x$enable_ruby" != "xno"],[
- PKG_CHECK_MODULES([RUBY], [ruby], [
- AC_SUBST([RUBY_CFLAGS])
- AC_SUBST([RUBY_LIBS])
- ],[
- AC_MSG_WARN([ruby not found])
- enable_ruby=no
- ])
-])
-AM_CONDITIONAL([HAVE_RUBY],[test "x$RUBY" != "xno" &&
- test "x$enable_ruby" = "xyes"])
-
dnl Check for Tcl, for embedding in the Tcl plugin.
AC_ARG_ENABLE([tcl],
[AS_HELP_STRING([--disable-tcl], [disable Tcl plugin])],
@@ -1617,7 +1598,6 @@ AC_CONFIG_FILES([Makefile
plugins/perl/Makefile
plugins/python/Makefile
plugins/random/Makefile
- plugins/ruby/Makefile
plugins/rust/Makefile
plugins/S3/Makefile
plugins/sh/Makefile
@@ -1747,7 +1727,6 @@ feature "lua" test "x$HAVE_LUA_TRUE" = "x"
feature "ocaml" test "x$HAVE_OCAML_TRUE" = "x"
feature "perl" test "x$HAVE_PERL_TRUE" = "x"
feature "python" test "x$HAVE_PYTHON_TRUE" = "x"
-feature "ruby" test "x$HAVE_RUBY_TRUE" = "x"
feature "rust" test "x$HAVE_RUST_TRUE" = "x"
feature "tcl" test "x$HAVE_TCL_TRUE" = "x"
diff --git a/plugins/ruby/Makefile.am b/plugins/ruby/Makefile.am
deleted file mode 100644
index 6b34cdf6fd..0000000000
--- a/plugins/ruby/Makefile.am
+++ /dev/null
@@ -1,83 +0,0 @@
-# nbdkit
-# Copyright Red Hat
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# * Neither the name of Red Hat nor the names of its contributors may be
-# used to endorse or promote products derived from this software without
-# specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-
-include $(top_srcdir)/common-rules.mk
-
-EXTRA_DIST = \
- nbdkit-ruby-plugin.pod \
- example.rb \
- $(NULL)
-
-if HAVE_RUBY
-
-plugin_LTLIBRARIES = nbdkit-ruby-plugin.la
-
-nbdkit_ruby_plugin_la_SOURCES = \
- ruby.c \
- $(top_srcdir)/include/nbdkit-plugin.h \
- $(NULL)
-
-nbdkit_ruby_plugin_la_CPPFLAGS = \
- -I$(top_srcdir)/include \
- -I$(top_builddir)/include \
- -I$(top_srcdir)/common/include \
- $(NULL)
-nbdkit_ruby_plugin_la_CFLAGS = \
- $(WARNINGS_CFLAGS) \
- $(RUBY_CFLAGS) \
- $(NULL)
-nbdkit_ruby_plugin_la_LIBADD = \
- $(IMPORT_LIBRARY_ON_WINDOWS) \
- $(RUBY_LIBS) \
- $(NULL)
-nbdkit_ruby_plugin_la_LDFLAGS = \
- -module -avoid-version -shared $(NO_UNDEFINED_ON_WINDOWS) \
- $(RUBY_LDFLAGS) \
- $(NULL)
-if USE_LINKER_SCRIPT
-nbdkit_ruby_plugin_la_LDFLAGS += \
- -Wl,--version-script=$(top_srcdir)/plugins/plugins.syms
-endif
-
-if HAVE_POD
-
-man_MANS = nbdkit-ruby-plugin.3
-CLEANFILES += $(man_MANS)
-
-nbdkit-ruby-plugin.3: nbdkit-ruby-plugin.pod \
- $(top_builddir)/podwrapper.pl
- $(PODWRAPPER) --section=3 --man $@ \
- --html $(top_builddir)/html/$@.html \
- $<
-
-endif HAVE_POD
-
-endif
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 48ddbd3117..2be1d0b652 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1314,34 +1314,6 @@ EXTRA_DIST += \
endif HAVE_PYTHON
-# Ruby plugin test.
-EXTRA_DIST += \
- shebang.rb \
- test.rb \
- test-shebang-ruby.sh \
- $(NULL)
-
-if HAVE_RUBY
-
-# Ruby tests are disabled. See "WARNING" section in
-# plugins/ruby/nbdkit-ruby-plugin.pod
-
-#LIBGUESTFS_TESTS += \
-# test-ruby \
-# $(NULL)
-#TESTS += test-shebang-ruby.sh
-EXTRA_PROGRAMS += test-ruby
-
-test_ruby_SOURCES = test-lang-plugins.c test.h
-test_ruby_CFLAGS = \
- -DLANG='"ruby"' -DSCRIPT='"$(srcdir)/test.rb"' \
- $(WARNINGS_CFLAGS) \
- $(LIBGUESTFS_CFLAGS) \
- $(NULL)
-test_ruby_LDADD = libtest.la $(LIBGUESTFS_LIBS)
-
-endif HAVE_RUBY
-
# Shell (sh) plugin test.
LIBGUESTFS_TESTS += test-shell
check_DATA += test-shell.img
diff --git a/plugins/ruby/ruby.c b/plugins/ruby/ruby.c
deleted file mode 100644
index d37b6061ef..0000000000
--- a/plugins/ruby/ruby.c
+++ /dev/null
@@ -1,549 +0,0 @@
-/* nbdkit
- * Copyright Red Hat
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of Red Hat nor the names of its contributors may be
- * used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-
-#include <nbdkit-plugin.h>
-
-#include <ruby.h>
-#ifdef HAVE_RUBY_VERSION_H
-#include <ruby/version.h>
-#endif
-
-#include "array-size.h"
-
-static VALUE nbdkit_module = Qnil;
-static int last_error;
-
-static VALUE
-set_error (VALUE self, VALUE arg)
-{
- int err;
- VALUE v;
-
- if (TYPE (arg) == T_CLASS) {
- v = rb_const_get (arg, rb_intern ("Errno"));
- err = NUM2INT (v);
- } else if (TYPE (arg) == T_OBJECT) {
- v = rb_funcall (arg, rb_intern ("errno"), 0);
- err = NUM2INT (v);
- } else {
- err = NUM2INT (arg);
- }
- last_error = err;
- nbdkit_set_error (err);
- return Qnil;
-}
-
-static void
-plugin_rb_load (void)
-{
- RUBY_INIT_STACK;
- ruby_init ();
- ruby_init_loadpath ();
-
- nbdkit_module = rb_define_module ("Nbdkit");
- rb_define_module_function (nbdkit_module, "set_error", set_error, 1);
-}
-
-/* https://stackoverflow.com/questions/11086549/how-to-rb-protect-everything... */
-#define MAX_ARGS 16
-struct callback_data {
- VALUE receiver; /* object being called */
- ID method_id; /* method on object being called */
- int argc; /* number of args */
- VALUE argv[MAX_ARGS]; /* list of args */
-};
-
-static VALUE
-callback_dispatch (VALUE datav)
-{
- struct callback_data *data = (struct callback_data *) datav;
- return rb_funcall2 (data->receiver, data->method_id, data->argc, data->argv);
-}
-
-enum exception_class {
- NO_EXCEPTION = 0,
- EXCEPTION_NO_METHOD_ERROR,
- EXCEPTION_OTHER,
-};
-
-static VALUE
-funcall2 (VALUE receiver, ID method_id, int argc, volatile VALUE *argv,
- enum exception_class *exception_happened)
-{
- struct callback_data data;
- size_t i, len;
- int state = 0;
- volatile VALUE ret, exn, message, backtrace, b;
-
- assert (argc <= MAX_ARGS);
-
- data.receiver = receiver;
- data.method_id = method_id;
- data.argc = argc;
- for (i = 0; i < argc; ++i)
- data.argv[i] = argv[i];
-
- ret = rb_protect (callback_dispatch, (VALUE) &data, &state);
- if (state) {
- /* An exception was thrown. Get the per-thread exception. */
- exn = rb_errinfo ();
-
- /* We treat NoMethodError specially. */
- if (rb_obj_is_kind_of (exn, rb_eNoMethodError)) {
- if (exception_happened)
- *exception_happened = EXCEPTION_NO_METHOD_ERROR;
- }
- else {
- if (exception_happened)
- *exception_happened = EXCEPTION_OTHER;
-
- /* Print the exception. */
- message = rb_funcall (exn, rb_intern ("to_s"), 0);
- nbdkit_error ("ruby: %s", StringValueCStr (message));
-
- /* Try to print the backtrace (a list of strings) if it exists. */
- backtrace = rb_funcall (exn, rb_intern ("backtrace"), 0);
- if (! NIL_P (backtrace)) {
- len = RARRAY_LEN (backtrace);
- for (i = 0; i < len; ++i) {
- b = rb_ary_entry (backtrace, i);
- nbdkit_error ("ruby: frame #%zu %s", i, StringValueCStr (b));
- }
- }
- }
-
- /* Reset the current thread exception. */
- rb_set_errinfo (Qnil);
- return Qnil;
- }
- else {
- if (exception_happened)
- *exception_happened = NO_EXCEPTION;
- return ret;
- }
-}
-
-static const char *script = NULL;
-static void *code = NULL;
-
-static void
-plugin_rb_unload (void)
-{
- if (ruby_cleanup (0) != 0)
- nbdkit_error ("ruby_cleanup failed");
-}
-
-static void
-plugin_rb_dump_plugin (void)
-{
-#ifdef RUBY_API_VERSION_MAJOR
- printf ("ruby_api_version=%d", RUBY_API_VERSION_MAJOR);
-#ifdef RUBY_API_VERSION_MINOR
- printf (".%d", RUBY_API_VERSION_MINOR);
-#ifdef RUBY_API_VERSION_TEENY
- printf (".%d", RUBY_API_VERSION_TEENY);
-#endif
-#endif
- printf ("\n");
-#endif
-
- if (!script)
- return;
-
- assert (code != NULL);
-
- (void) funcall2 (Qnil, rb_intern ("dump_plugin"), 0, NULL, NULL);
-}
-
-static int
-plugin_rb_config (const char *key, const char *value)
-{
- /* The first parameter must be "script". */
- if (!script) {
- int state;
-
- if (strcmp (key, "script") != 0) {
- nbdkit_error ("the first parameter must be "
- "script=/path/to/ruby/script.rb");
- return -1;
- }
- script = value;
-
- nbdkit_debug ("ruby: loading script %s", script);
-
- /* Load the Ruby script into the interpreter. */
- const char *options[] = { "--", script };
- code = ruby_options (ARRAY_SIZE (options), (char **) options);
-
- /* Check if we managed to compile the Ruby script to code. */
- if (!ruby_executable_node (code, &state)) {
- nbdkit_error ("could not compile ruby script (%s, state=%d)",
- script, state);
- return -1;
- }
-
- /* Execute the Ruby script. */
- state = ruby_exec_node (code);
- if (state) {
- nbdkit_error ("could not execute ruby script (%s, state=%d)",
- script, state);
- return -1;
- }
-
- return 0;
- }
- else {
- volatile VALUE argv[2];
- enum exception_class exception_happened;
-
- argv[0] = rb_str_new2 (key);
- argv[1] = rb_str_new2 (value);
- (void) funcall2 (Qnil, rb_intern ("config"), 2, argv, &exception_happened);
- if (exception_happened == EXCEPTION_NO_METHOD_ERROR) {
- /* No config method, emulate what core nbdkit does if the
- * config callback is NULL.
- */
- nbdkit_error ("%s: this plugin does not need command line configuration",
- script);
- return -1;
- }
- else if (exception_happened == EXCEPTION_OTHER)
- return -1;
-
- return 0;
- }
-}
-
-static int
-plugin_rb_config_complete (void)
-{
- enum exception_class exception_happened;
-
- if (!script) {
- nbdkit_error ("the first parameter must be script=/path/to/ruby/script.rb");
- return -1;
- }
-
- assert (code != NULL);
-
- (void) funcall2 (Qnil, rb_intern ("config_complete"), 0, NULL,
- &exception_happened);
- if (exception_happened == EXCEPTION_NO_METHOD_ERROR)
- return 0; /* no config_complete method defined, ignore */
- else if (exception_happened == EXCEPTION_OTHER)
- return -1;
-
- return 0;
-}
-
-static void *
-plugin_rb_open (int readonly)
-{
- volatile VALUE argv[1];
- volatile VALUE rv;
- enum exception_class exception_happened;
-
- argv[0] = readonly ? Qtrue : Qfalse;
- rv = funcall2 (Qnil, rb_intern ("open"), 1, argv, &exception_happened);
- if (exception_happened == EXCEPTION_NO_METHOD_ERROR) {
- nbdkit_error ("%s: missing callback: %s", script, "open");
- return NULL;
- }
- else if (exception_happened == EXCEPTION_OTHER)
- return NULL;
-
- return (void *) rv;
-}
-
-static void
-plugin_rb_close (void *handle)
-{
- volatile VALUE argv[1];
-
- argv[0] = (VALUE) handle;
- (void) funcall2 (Qnil, rb_intern ("close"), 1, argv, NULL);
- /* OK to ignore exceptions here, if they are important then an error
- * was printed already.
- */
-}
-
-static int64_t
-plugin_rb_get_size (void *handle)
-{
- volatile VALUE argv[1];
- volatile VALUE rv;
- enum exception_class exception_happened;
-
- argv[0] = (VALUE) handle;
- rv = funcall2 (Qnil, rb_intern ("get_size"), 1, argv, &exception_happened);
- if (exception_happened == EXCEPTION_NO_METHOD_ERROR) {
- nbdkit_error ("%s: missing callback: %s", script, "get_size");
- return -1;
- }
- else if (exception_happened == EXCEPTION_OTHER)
- return -1;
-
- return NUM2ULL (rv);
-}
-
-static int
-plugin_rb_pread (void *handle, void *buf,
- uint32_t count, uint64_t offset)
-{
- volatile VALUE argv[3];
- volatile VALUE rv;
- enum exception_class exception_happened;
-
- argv[0] = (VALUE) handle;
- argv[1] = ULL2NUM (count);
- argv[2] = ULL2NUM (offset);
- rv = funcall2 (Qnil, rb_intern ("pread"), 3, argv, &exception_happened);
- if (exception_happened == EXCEPTION_NO_METHOD_ERROR) {
- nbdkit_error ("%s: missing callback: %s", script, "pread");
- return -1;
- }
- else if (exception_happened == EXCEPTION_OTHER)
- return -1;
-
- if (RSTRING_LEN (rv) < count) {
- nbdkit_error ("%s: byte array returned from pread is too small",
- script);
- return -1;
- }
-
- memcpy (buf, RSTRING_PTR (rv), count);
- return 0;
-}
-
-static int
-plugin_rb_pwrite (void *handle, const void *buf,
- uint32_t count, uint64_t offset)
-{
- volatile VALUE argv[3];
- enum exception_class exception_happened;
-
- argv[0] = (VALUE) handle;
- argv[1] = rb_str_new (buf, count);
- argv[2] = ULL2NUM (offset);
- (void) funcall2 (Qnil, rb_intern ("pwrite"), 3, argv, &exception_happened);
- if (exception_happened == EXCEPTION_NO_METHOD_ERROR) {
- nbdkit_error ("%s: missing callback: %s", script, "pwrite");
- return -1;
- }
- else if (exception_happened == EXCEPTION_OTHER)
- return -1;
-
- return 0;
-}
-
-static int
-plugin_rb_flush (void *handle)
-{
- volatile VALUE argv[1];
- enum exception_class exception_happened;
-
- argv[0] = (VALUE) handle;
- (void) funcall2 (Qnil, rb_intern ("flush"), 1, argv, &exception_happened);
- if (exception_happened == EXCEPTION_NO_METHOD_ERROR) {
- nbdkit_error ("%s: not implemented: %s", script, "flush");
- return -1;
- }
- else if (exception_happened == EXCEPTION_OTHER)
- return -1;
-
- return 0;
-}
-
-static int
-plugin_rb_trim (void *handle, uint32_t count, uint64_t offset)
-{
- volatile VALUE argv[3];
- enum exception_class exception_happened;
-
- argv[0] = (VALUE) handle;
- argv[1] = ULL2NUM (count);
- argv[2] = ULL2NUM (offset);
- (void) funcall2 (Qnil, rb_intern ("trim"), 3, argv, &exception_happened);
- if (exception_happened == EXCEPTION_NO_METHOD_ERROR) {
- nbdkit_error ("%s: not implemented: %s", script, "trim");
- return -1;
- }
- else if (exception_happened == EXCEPTION_OTHER)
- return -1;
-
- return 0;
-}
-
-static int
-plugin_rb_zero (void *handle, uint32_t count, uint64_t offset, int may_trim)
-{
- volatile VALUE argv[4];
- enum exception_class exception_happened;
-
- argv[0] = (VALUE) handle;
- argv[1] = ULL2NUM (count);
- argv[2] = ULL2NUM (offset);
- argv[3] = may_trim ? Qtrue : Qfalse;
- last_error = 0;
- (void) funcall2 (Qnil, rb_intern ("zero"), 4, argv, &exception_happened);
- if (last_error == EOPNOTSUPP || last_error == ENOTSUP ||
- exception_happened == EXCEPTION_NO_METHOD_ERROR) {
- nbdkit_debug ("zero falling back to pwrite");
- nbdkit_set_error (EOPNOTSUPP);
- return -1;
- }
- else if (exception_happened == EXCEPTION_OTHER)
- return -1;
-
- return 0;
-}
-
-static int
-plugin_rb_can_write (void *handle)
-{
- volatile VALUE argv[1];
- volatile VALUE rv;
- enum exception_class exception_happened;
-
- argv[0] = (VALUE) handle;
- rv = funcall2 (Qnil, rb_intern ("can_write"), 1, argv, &exception_happened);
- if (exception_happened == EXCEPTION_NO_METHOD_ERROR)
- /* Fall back to checking if the pwrite method exists. */
- rv = rb_funcall (Qnil, rb_intern ("respond_to?"),
- 2, ID2SYM (rb_intern ("pwrite")), Qtrue);
- else if (exception_happened == EXCEPTION_OTHER)
- return -1;
-
- return RTEST (rv);
-}
-
-static int
-plugin_rb_can_flush (void *handle)
-{
- volatile VALUE argv[1];
- volatile VALUE rv;
- enum exception_class exception_happened;
-
- argv[0] = (VALUE) handle;
- rv = funcall2 (Qnil, rb_intern ("can_flush"), 1, argv, &exception_happened);
- if (exception_happened == EXCEPTION_NO_METHOD_ERROR)
- /* Fall back to checking if the flush method exists. */
- rv = rb_funcall (Qnil, rb_intern ("respond_to?"),
- 2, ID2SYM (rb_intern ("flush")), Qtrue);
- else if (exception_happened == EXCEPTION_OTHER)
- return -1;
-
- return RTEST (rv);
-}
-
-static int
-plugin_rb_is_rotational (void *handle)
-{
- volatile VALUE argv[1];
- volatile VALUE rv;
- enum exception_class exception_happened;
-
- argv[0] = (VALUE) handle;
- rv = funcall2 (Qnil, rb_intern ("is_rotational"), 1, argv,
- &exception_happened);
- if (exception_happened == EXCEPTION_NO_METHOD_ERROR)
- return 0;
- else if (exception_happened == EXCEPTION_OTHER)
- return -1;
-
- return RTEST (rv);
-}
-
-static int
-plugin_rb_can_trim (void *handle)
-{
- volatile VALUE argv[1];
- volatile VALUE rv;
- enum exception_class exception_happened;
-
- argv[0] = (VALUE) handle;
- rv = funcall2 (Qnil, rb_intern ("can_trim"), 1, argv, &exception_happened);
- if (exception_happened == EXCEPTION_NO_METHOD_ERROR)
- /* Fall back to checking if the trim method exists. */
- rv = rb_funcall (Qnil, rb_intern ("respond_to?"),
- 2, ID2SYM (rb_intern ("trim")), Qtrue);
- else if (exception_happened == EXCEPTION_OTHER)
- return -1;
-
- return RTEST (rv);
-}
-
-#define plugin_rb_config_help \
- "script=<FILENAME> (required) The Ruby plugin to run.\n" \
- "[other arguments may be used by the plugin that you load]"
-
-/* Ruby is inherently unsafe to call in parallel from multiple
- * threads.
- */
-#define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS
-
-static struct nbdkit_plugin plugin = {
- .name = "ruby",
- .version = PACKAGE_VERSION,
-
- .load = plugin_rb_load,
- .unload = plugin_rb_unload,
- .dump_plugin = plugin_rb_dump_plugin,
-
- .config = plugin_rb_config,
- .config_complete = plugin_rb_config_complete,
- .config_help = plugin_rb_config_help,
-
- .open = plugin_rb_open,
- .close = plugin_rb_close,
-
- .get_size = plugin_rb_get_size,
- .can_write = plugin_rb_can_write,
- .can_flush = plugin_rb_can_flush,
- .is_rotational = plugin_rb_is_rotational,
- .can_trim = plugin_rb_can_trim,
-
- .pread = plugin_rb_pread,
- .pwrite = plugin_rb_pwrite,
- .flush = plugin_rb_flush,
- .trim = plugin_rb_trim,
- .zero = plugin_rb_zero,
-};
-
-NBDKIT_REGISTER_PLUGIN (plugin)
diff --git a/plugins/ruby/example.rb b/plugins/ruby/example.rb
deleted file mode 100644
index b02d5ba498..0000000000
--- a/plugins/ruby/example.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-# Example Ruby plugin.
-#
-# This example can be freely used for any purpose.
-
-# Run it from the build directory like this:
-#
-# ./nbdkit -f -v ruby ./plugins/ruby/example.rb test1=foo test2=bar
-#
-# Or run it after installing nbdkit like this:
-#
-# nbdkit -f -v ruby ./plugins/ruby/example.rb test1=foo test2=bar
-#
-# The -f -v arguments are optional. They cause the server to stay in
-# the foreground and print debugging, which is useful when testing.
-#
-# You can connect to the server using guestfish or qemu, eg:
-#
-# guestfish --format=raw -a nbd://localhost
-# ><fs> run
-# ><fs> part-disk /dev/sda mbr
-# ><fs> mkfs ext2 /dev/sda1
-# ><fs> list-filesystems
-# ><fs> mount /dev/sda1 /
-# ><fs> [etc]
-
-include Nbdkit
-
-$disk = "\0" * (1024 * 1024)
-
-def config(key, value)
- printf("%s = %s\n", key, value)
-end
-
-def open(readonly)
- # You can return any non-nil Ruby object as a handle. The
- # same object is passed as the first argument to the other
- # callbacks.
- h = {}
- return h
-end
-
-def get_size(h)
- return $disk.bytesize
-end
-
-def pread(h, count, offset)
- return $disk.byteslice(offset, count)
-end
-
-def pwrite(h, buf, offset)
- # Hmm, is this using bytes or chars? XXX
- $disk[offset, buf.length] = buf
-end
-
-def zero(h, count, offset, may_trim)
- if may_trim
- $disk[offset, count] = "\0" * count
- else
- set_error(Errno::EOPNOTSUPP)
- raise "falling back to pwrite"
- end
-end
diff --git a/tests/test-dump-plugin.sh b/tests/test-dump-plugin.sh
index a7a347b38b..fdf28f0993 100755
--- a/tests/test-dump-plugin.sh
+++ b/tests/test-dump-plugin.sh
@@ -43,7 +43,7 @@ do_test ()
{
vg=; [ "$NBDKIT_VALGRIND" = "1" ] && vg="-valgrind"
case "$1$vg" in
- python-valgrind | ruby-valgrind | tcl-valgrind)
+ python-valgrind | tcl-valgrind)
echo "$0: skipping $1$vg because this language doesn't support valgrind"
;;
example4*)
diff --git a/tests/test-help-plugin.sh b/tests/test-help-plugin.sh
index 3d06808ead..19959716cc 100755
--- a/tests/test-help-plugin.sh
+++ b/tests/test-help-plugin.sh
@@ -43,7 +43,7 @@ do_test ()
{
vg=; [ "$NBDKIT_VALGRIND" = "1" ] && vg="-valgrind"
case "$1$vg" in
- python-valgrind | ruby-valgrind | tcl-valgrind)
+ python-valgrind | tcl-valgrind)
echo "$0: skipping $1$vg because this language doesn't support valgrind"
;;
example4*)
diff --git a/tests/test-no-parameters.sh b/tests/test-no-parameters.sh
index c1ffbc192e..8db6464cbd 100755
--- a/tests/test-no-parameters.sh
+++ b/tests/test-no-parameters.sh
@@ -67,8 +67,7 @@ do_test ()
vg=; [ "$NBDKIT_VALGRIND" = "1" ] && vg="-valgrind"
case "$1$vg" in
example4-valgrind | gcs-valgrind | lua-valgrind | \
- python-valgrind | ruby-valgrind | \
- S3-valgrind | tcl-valgrind)
+ python-valgrind | S3-valgrind | tcl-valgrind)
echo "$0: skipping $1$vg because this language doesn't support valgrind"
;;
example4)
diff --git a/tests/test-plugin-docs.sh b/tests/test-plugin-docs.sh
index 7498b7b0b3..1d79a64587 100755
--- a/tests/test-plugin-docs.sh
+++ b/tests/test-plugin-docs.sh
@@ -94,7 +94,7 @@ run_test ()
do_test ()
{
case "$1" in
- cc|eval|golang|lua|ocaml|perl|python|ruby|rust|sh|tcl)
+ cc|eval|golang|lua|ocaml|perl|python|rust|sh|tcl)
# Skip all language plugins as this test doesn't
# make sense for these.
;;
diff --git a/tests/test-shebang-ruby.sh b/tests/test-shebang-ruby.sh
deleted file mode 100755
index 0e5ad64cd2..0000000000
--- a/tests/test-shebang-ruby.sh
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env bash
-# nbdkit
-# Copyright Red Hat
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# * Neither the name of Red Hat nor the names of its contributors may be
-# used to endorse or promote products derived from this software without
-# specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-
-source ./functions.sh
-
-pidfile=shebang-ruby.pid
-sock=$(mktemp -u /tmp/nbdkit-test-sock.XXXXXX)
-script=./shebang.rb
-
-rm -f $pidfile $sock
-cleanup_fn rm -f $pidfile $sock
-
-$script -P $pidfile -U $sock -f -v &
-
-# We may have to wait a short time for the pid file to appear.
-for i in {1..60}; do
- if test -f $pidfile; then
- break
- fi
- sleep 1
-done
-if ! test -f $pidfile; then
- echo "$0: PID file was not created"
- exit 1
-fi
-
-pid="$(cat $pidfile)"
-cleanup_fn kill $pid
-
-# Check the process exists.
-kill -s 0 $pid
-
-# Check the socket was created (and is a socket).
-test -S $sock
-
-# Kill the process.
-kill $pid
-
-# Check the process exits (eventually).
-for i in {1..10}; do
- if ! kill -s 0 $pid; then
- break;
- fi
- sleep 1
-done
-if kill -s 0 $pid; then
- echo "$0: process did not exit after sending a signal"
- exit 1
-fi
diff --git a/tests/test-version-plugin.sh b/tests/test-version-plugin.sh
index f952dc6cff..ca70b3cc8d 100755
--- a/tests/test-version-plugin.sh
+++ b/tests/test-version-plugin.sh
@@ -43,7 +43,7 @@ do_test ()
{
vg=; [ "$NBDKIT_VALGRIND" = "1" ] && vg="-valgrind"
case "$1$vg" in
- python-valgrind | ruby-valgrind | tcl-valgrind)
+ python-valgrind | tcl-valgrind)
echo "$0: skipping $1$vg because this language doesn't support valgrind"
;;
example4*)
diff --git a/tests/shebang.rb b/tests/shebang.rb
deleted file mode 100755
index 34588d1a8d..0000000000
--- a/tests/shebang.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-#!../nbdkit ruby
-
-include Nbdkit
-
-$disk = "\0" * (1024 * 1024)
-
-def open(readonly)
- h = {}
- return h
-end
-
-def get_size(h)
- return $disk.bytesize
-end
-
-def pread(h, count, offset)
- return $disk.byteslice(offset, count)
-end
diff --git a/tests/test-lang-plugins.c b/tests/test-lang-plugins.c
index e29e261fb2..934b293434 100644
--- a/tests/test-lang-plugins.c
+++ b/tests/test-lang-plugins.c
@@ -55,9 +55,7 @@ main (int argc, char *argv[])
* valgrind, so skip them.
*/
s = getenv ("NBDKIT_VALGRIND");
- if (s && strcmp (s, "1") == 0 &&
- (strcmp (LANG, "ruby") == 0 ||
- strcmp (LANG, "tcl") == 0)) {
+ if (s && strcmp (s, "1") == 0 && strcmp (LANG, "tcl") == 0) {
fprintf (stderr, "%s test skipped under valgrind.\n", LANG);
exit (77); /* Tells automake to skip the test. */
}
diff --git a/.gitignore b/.gitignore
index 28c9bf935d..ba75e7eaae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -172,7 +172,6 @@ plugins/*/*.3
/tests/test-random
/tests/test-readahead
/tests/test-retry-request-mirror
-/tests/test-ruby
/tests/test-shell
/tests/test-shell.img
/tests/test-socket-activation
diff --git a/README.md b/README.md
index 725e6ab920..f266594b23 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ The key features of nbdkit are:
libraries or included in proprietary code.
* Well-documented, simple plugin API with a stable ABI guarantee.
Lets you export “unconventional” block devices easily.
-* You can write plugins in C/C++, Go, Lua, Perl, Python, OCaml, Ruby,
+* You can write plugins in C/C++, Go, Lua, Perl, Python, OCaml,
Rust, shell script or Tcl.
* Filters can be stacked in front of plugins to transform the output.
diff --git a/ci/build.sh b/ci/build.sh
index 6bf052c4f1..2167ef032e 100755
--- a/ci/build.sh
+++ b/ci/build.sh
@@ -83,7 +83,6 @@ main() {
--disable-libfuzzer
--enable-plugins
--enable-python
---enable-ruby
--enable-tcl
--enable-lua
--enable-torrent
--
2.44.0
7 months, 1 week
Re: [PATCH nbdkit] Remove nbdkit-ruby-plugin and Ruby language support completely
by Richard W.M. Jones
On Wed, Apr 17, 2024 at 12:42:42PM +0100, Daniel P. Berrangé wrote:
> On Tue, Apr 16, 2024 at 10:22:32PM +0100, Richard W.M. Jones wrote:
> > This has not worked for a long time because of a fundamental bug in
> > how Ruby's garbage collector interacts with pthread stacks. There has
> > been a bug open for a long time (over 14 years) which has never been
> > fixed:
> >
> > https://redmine.ruby-lang.org/issues/2294#note-34
> >
> > As that discussion is rather hard to follow, the basic problem is
> > this:
> >
> > When you embed Ruby you must tell the interpreter where "the stack"
> > is. You do this by calling RUBY_INIT_STACK early on (which we do in
> > load(), on the main thread). This marks the top of stack.
> >
> > From time to time Ruby's garbage collector will scan the stack
> > starting at the current stack pointer, and going up til it reaches the
> > previously marked top of stack. It scans this memory looking for
> > roots etc.
> >
> > This strategy fails completely if you have threads. In a thread
> > (using a different stack entirely) it scans randomly across the heap,
> > resulting in weird behaviour at best, but more usually straight
> > crashes.
>
> Urgh, horrible.
>
> >
> > We've had a Ruby plugin for a long time, but it broke with Ruby 1.9,
> > when Ruby changed from some kind of cooperative threading to using
> > native threads (and therefore separate stacks).
> >
> > The tests have been disabled since early 2018 (nbdkit < 1.2). I
> > re-enabled the tests just now (Ruby 3.3) but it's broken in the same
> > way as ever.
> >
> > The only changes that have happened since the tests were disabled are
> > mechanical ones / project-wide cleanups.
> >
> > It currently crashes in open().
> >
> > It seems highly unlikely that anyone is using this plugin.
>
> IIUC, the '--threads' arg is just a per-client limit. So if
> there are multiple clients, you'll still get many threads
> using ruby even with "--threads = 1".
Yes, and in fact you'll get an extra thread even with one client.
Using a background thread outside of nbdkit's control and funnelling
requests through that (see my follow up) seems to be the only way.
> If spawning nbdkit with --single though, you'll get a separate
> process per client, and then with --threads=1, you could can
> sure that only 1 thread is calling into ruby.
True, although --single is of very limited use. Basically it forces
you to use xinetd or systemd or libnbd's nbd_connect_command.
> Is there a way a plugin can declare that it is non-threadsafe,
> and thus allow nbdkit to refuse to run it without --single
> and --threads=1, being set ?
It's possible, but the use case would be so limiting that I'm not sure
if the cure is better than the disease.
> I guess that will complicate your test suite though unless
> you can skip all tests which rely on multi-connections and
> threads.
Rich.
> With regards,
> Daniel
> --
> |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org -o- https://fstop138.berrange.com :|
> |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-builder quickly builds VMs from scratch
http://libguestfs.org/virt-builder.1.html
7 months, 1 week