Cannot parse Ruby VALUE type from C/C++

I think I may have found a bug in the Ruby interpreter, but I’m not sure.

Anytime I receive a VALUE type within a C function which is called from Ruby, the extension completely stalls if I try to access its native data.

What happens is that I’ll call a function from the Ruby interpreter, say StringValueCStr(...) or NUM2INT(...): the interpreter then receives it, and a few seconds later, the callstack shows the process waiting within one of Windows’ core OS .DLLs; the top-most level function is the Win32 API’s WaitForSingleObject (i.e., it’s waiting for a thread or something else), which sounds like some sort of stall.

Once the initial function call is made to parse the input data received, the native function called from Ruby’s interpreter doesn’t finish its path.

In other words, if one sets a breakpoint on the function call, and then a breakpoint somewhere after it within the same function, that second breakpoint is never hit.

So far I’ve tested this with integer parsing as well as strings. I’ve also tested this both in my own extension as well as one of the SDK examples.

I’m not sure if anyone else has experienced this, or if it’s reproducible on anyone else’s machine. However, I would be grateful if someone would be willing to try, with some simple modifications to the licensed_ruby_extension example:

in licensed_ruby_extension.cpp,

// Implementation of SUEX_Licensed.do_work
static VALUE DoWork(VALUE someString) { //<--- parameter is added to the function signature
	Sleep(15000); // I use this as it allows me time to attach Visual Studio's debugger to the extension on load
	const char* str = StringValueCStr(someString); // Here is the kicker

  if (!CheckLicense()) {
    // We aren't allowed to run. Raising a ruby error here for demonstration
    // purposes but a well-behaved extension would display a user-friendly
    // message in this case.
    rb_raise(rb_eRuntimeError, "This extension is not licensed.");
    return Qnil;
  }
  // We are good to run. Do the work here.
  // ...
  return Qtrue;
}

// Entry point of the C extension.
extern "C" CEXT_EXPORT
void Init_licensed_ruby_extension()
{
  VALUE mSUEX_HelloWorld = rb_define_module("SUEX_Licensed");
  rb_define_module_function(mSUEX_HelloWorld, "do_work",
      VALUEFUNC(DoWork), 1); //<--- the '1' (originally 0) is new: it states that the param count of DoWork is  '1'
}

In loader.rb:

def self.execute
  # Run some code from the C extension
  if Sketchup.respond_to?(:is_64bit?) && Sketchup.is_64bit?
    # Load 64-bit binary.
    require_relative "bin64/licensed_ruby_extension"
  else
    # Load 32-bit binary.
    require_relative "bin/licensed_ruby_extension"
  end
  if (SUEX_Licensed.do_work("Hello to you!")) <-- added string argument (only change made)
    UI.messagebox("Success!")
  else
    UI.messagebox("FAILED!")
  end
end

For the repro, the compiler used was MSVC 2010 with a 32-bit target architecture.

Thoughts? Experiences?

Can you try adding the self parameter to the function:

static VALUE DoWork(VALUE self, VALUE someString)

?

1 Like

I second Bugra’s question - there should always be a self parameter in these functions.

Wow, that was a fumble on my part. It did indeed fix the issue; thanks, and sorry for the overkill.