Skip to content

Latest commit

 

History

History
 
 

docs

## JRuby implementation of Ruby C extensions API.

This document explains the state of C extensions on JRuby. This is
meant to become an exhaustive listing of the restrictions in the C API
layer, their reasons, and how to work around them.

We also provide code examples for some workarounds at the end of this
document.

If more restrictions are found, or you have code examples, please feel
free to add them.

### Objectives

Provide a highly compatible implementation of C extensions on
JRuby. The goal here is not performance, but compatibility. We do not
recommend running Ruby C extensions in production, at least not for a
longer period of time, and we probably never will. The idea here is
that if you're transitioning to JRuby, you can run your C extensions
until you find something more suitable. The focus is also very clearly
on Gems that have no JRuby specific version available. The C API is
implemented as a JNI plugin to the JVM.

### Restrictions

#### Runtime

JRuby supports multiple "Runtimes" in a single process. Because you
can only load a dynamic/shared library into the JVM once, and we
cannot be sure whether a C extension is thread-safe, so we disallow
loading C extensions in more than one runtime. To work around this,
you may disable JRuby's in-process execution (default from 1.6.5
onwards).

#### Strings

* **RSTRING_PTR(VALUE)**

    *Problem:* In order to support direct manipulation of C array
     backed strings, whenever you call this macro, the object
     referenced by VALUE is added to a synchronization list, and
     whenever you transition between C and Java land, the contents of
     the Java and C string are copied back and forth, adding a
     potentially significant overhead.  The lifetime of the C memory
     referenced by the result of RSTRING_PTR is the same as the
     garbage collection lifetime of the corresponding String object,
     so it will only go off the synchronization list if the associated
     object is collected.

    *Workaround:* To change bytes in a String object, use one of

	void rb_str_update(VALUE str, long beg, long len, VALUE val);
	VALUE rb_str_buf_append(VALUE str, VALUE val);
	VALUE rb_str_buf_cat(VALUE str, const char* bytes, long len);
	VALUE rb_str_buf_cat2(VALUE str, const char* cstring);
	VALUE rb_str_append(VALUE str, VALUE arg);

#### Arrays

* **RARRAY_PTR**

    *Problem:* The same runtime overhead implications as for RSTRING_PTR apply.

    *Workaround:* To access elements of an Array, use rb_ary_entry or
    rb_ary_store which are also available in MagLev and Rubinius.

#### IO

* **rb_io_wait_readable, rb_io_wait_writable, rb_io_check_readable, rb_io_check_writable, rb_io_check_closed, GetOpenFile**
    
    *Problem:* Mixing native file descriptors with JVM fds doesn't
     work very well. Sometimes, things will work just fine, but more
     often than not, these functions will simply fail.

    *Workaround:* Upcall to Ruby for dealing with IO and files.

#### Hash

* **rb_iterate_each_pair**, **rb_iterate**, **rb_each**

    *Problem:* Only supports iteration on Array arguments for now

    *Workaround:* You can coerce you're Hash into an array of pairs
     before iterating.

* **RHASH**, **RHASH_TBL**

    *Problem:* Like on MagLev and Rubinius, these macros aren't supported.

#### Threads

* **thread_critical**

    *Problem:* Not supported, setting this to `true` is ignored

#### Globals and GC

* **Object lifetime**

    *Problem:* C-references aren't kept alive/valid after returning
    from C extension code.

    Each call from Ruby into a C extension initializes a list known to
    the garbage collector that is kept alive for the duration of that
    entry into a C extension. This list is dereferenced when returning
    from C back to Ruby.

    *Workaround:* For a newly created object to stay alive after
    returning from C to Ruby it must have been stored as the value of
    a Ruby global variable, Ruby constant, or stored into an instance
    variable of some other object reachable from top level Ruby state,
    or be reachable from the VALUE returned from C back to Ruby.

#### Numerics

* **rb_num2ulong**
  
  *Problem:* Java longs are of a different size than whatever the
   native long size may be on the machine. This can be observed in the
   spec failure "rb_num2ulong converts -1 to an unsigned number" in
   the Ruby specs.

  *Workaround:* None.

### Bugs and status of rubyspecs (as of 30 Sep 2011)

For the rubyspecs in optional/capi/ , the following specs are skipped
and are not supported

 * rb_define_hooked_variable
 * rb_define_variable
 * rb_protect_inspect
 * rb_inspecting_p
 * rb_exec_recursive

The following functions have known bugs that we will try to fix

 * rb_class_new fails to throw an error when passed a singleton class as superclass
 * rb_rescue has some spec failures
 * rb_thread_select fails to detect an fd that's ready to read
 * rb_str_buf_* sometimes fails to synchronize the C buffer correctly