Exemple #1
0
  VALUE rb_struct_define(const char *name, ...) {
    va_list args;
    va_start(args, name);

    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    VALUE nm;
    if(name) {
      nm = rb_str_new2(name);
    } else {
      nm = Qnil;
    }

    char *sym;
    Array* array = Array::create(env->state(), 0);
    while((sym = va_arg(args, char*)) != 0) {
      array->append(env->state(), env->get_object(rb_intern(sym)));
    }

    va_end(args);

    return rb_funcall(rb_cStruct, rb_intern("make_struct"), 2, nm, env->get_handle(array));
  }
Exemple #2
0
  unsigned long rb_big2ulong(VALUE obj) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Object* object = env->get_object(obj);

    if(object->nil_p()) {
      rb_raise(rb_eTypeError, "no implicit conversion from nil to unsigned long");
    } else if(Bignum* big = try_as<Bignum>(object)) {
      if((size_t)mp_count_bits(big->mp_val()) > sizeof(long) * CHAR_BIT)
        rb_raise(rb_eRangeError, "bignum too big to convert into long");

      unsigned long val = big->to_ulong();
      if(big->mp_val()->sign == MP_NEG) {
        if((long)val < 0)
          rb_raise(rb_eRangeError, "bignum out of range of unsigned long");
        return -val;
      }
      return val;
    }
    rb_raise(rb_eArgError, "parameter is not a Bignum");

    return 0;
  }
Exemple #3
0
  VALUE capi_thread_create(VALUE (*func)(ANYARGS), void* arg, const char* name, const char* file) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    State* state = env->state();

    NativeMethod* nm = NativeMethod::create(state,
                        String::create(state, file), G(thread),
                        state->symbol(name), (void*)func,
                        Fixnum::from(1), 0);

    Pointer* ptr = Pointer::create(state, arg);

    Thread* thr = Thread::create(env->state(), G(thread), run_function);

    thr->locals_store(state, state->symbol("function"), nm);
    thr->locals_store(state, state->symbol("argument"), ptr);

    thr->group(state, state->vm()->thread.get()->group());

    VALUE thr_handle = env->get_handle(thr);
    thr->fork(state);

    return thr_handle;
  }
Exemple #4
0
  void* rb_thread_call_with_gvl(void* (*func)(void*), void* data) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    env->state()->shared().enter_capi(env->state());
    env->state()->gc_dependent();

    void* ret = (*func)(data);

    env->state()->gc_independent();
    env->state()->shared().leave_capi(env->state());

    return ret;
  }
Exemple #5
0
  /** @note   Shares code with rb_define_module_under, change there too. --rue */
  VALUE rb_define_class_under(VALUE parent_handle, const char* name, VALUE superclass_handle) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Module* parent = c_as<Module>(env->get_object(parent_handle));
    Class* superclass = c_as<Class>(env->get_object(superclass_handle));
    Symbol* constant = env->state()->symbol(name);

    bool created = false;
    Class* cls = rubinius::Helpers::open_class(env->state(),
        env->current_call_frame(), parent, superclass, constant, &created);

    return env->get_handle_global(cls);
  }
Exemple #6
0
  VALUE rb_const_get_from(VALUE module_handle, ID id_name) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    State* state = env->state();
    Symbol* name = reinterpret_cast<Symbol*>(id_name);
    Module* module = c_as<Module>(env->get_object(module_handle));

    ConstantMissingReason reason = vNonExistent;
    while(!module->nil_p()) {
      Object* val = module->get_const(state, name, G(sym_private), &reason);
      if(reason == vFound) {
        if(Autoload* autoload = try_as<Autoload>(val)) {
          return capi_fast_call(env->get_handle(autoload),
              rb_intern("call"), 1, module_handle);
        }

        return env->get_handle(val);
      }

      module = module->superclass();
    }

    return const_missing(module_handle, id_name);
  }
Exemple #7
0
  // THAR BE EVEN MORE DRAGONS.
  //
  // When venturing through the valleys of the unmanaged, our hero must
  // remain vigilant and disciplined! If she should ever find a VALUE for
  // a reference in her travels: Look away! For these VALUEs are pure
  // death! Our hero must steel herself and continue on her quest, returning
  // as soon as possible to the castle of the managed.
  void* rb_thread_call_without_gvl2(void *(*func)(void *data), void* data1,
                                   rb_unblock_function_t ubf, void* ubf_data) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    State* state = env->state();
    void* ret = NULL;

    if(!state->check_async(env->current_call_frame())) {
      return ret;
    }
    if(ubf == RUBY_UBF_IO || ubf == RUBY_UBF_PROCESS) {
      state->vm()->interrupt_with_signal();
    } else {
      state->vm()->wait_on_custom_function(ubf, ubf_data);
    }
    LEAVE_CAPI(env->state());
    {
      GCIndependent guard(env);
      ret = (*func)(data1);
    }
    ENTER_CAPI(env->state());
    state->vm()->clear_waiter();

    return ret;
  }
Exemple #8
0
  // THAR BE EVEN MORE DRAGONS.
  //
  // When venturing through the valleys of the unmanaged, our hero must
  // remain vigilant and disciplined! If she should ever find a VALUE for
  // a reference in her travels: Look away! For these VALUEs are pure
  // death! Our hero must steel herself and continue on her quest, returning
  // as soon as possible to the castle of the managed.
  void* rb_thread_call_without_gvl2(void *(*func)(void *data), void* data1,
                                   rb_unblock_function_t ubf, void* ubf_data) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    State* state = env->state();
    void* ret = NULL;

    if(state->vm()->thread_interrupted_p(state)) {
      return ret;
    }
    if(ubf == RUBY_UBF_IO || ubf == RUBY_UBF_PROCESS) {
      state->vm()->interrupt_with_signal();
    } else {
      state->vm()->wait_on_custom_function(ubf, ubf_data);
    }
    LEAVE_CAPI(env->state());
    {
      UnmanagedPhase unmanaged(env->state());
      ret = (*func)(data1);
    }
    ENTER_CAPI(env->state());
    state->vm()->clear_waiter();

    return ret;
  }
Exemple #9
0
  /*
   * rb_thread_wait_fd actually waits until a read is
   * available on the given fd
   */
  void rb_thread_wait_fd(int fd) {
    fd_set fds;

    FD_ZERO(&fds);
    FD_SET((int_fd_t)fd, &fds);

    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    State* state = env->state();
    LEAVE_CAPI(state);
    {
      GCIndependent guard(env);
      state->vm()->interrupt_with_signal();
      state->vm()->thread->sleep(state, cTrue);

      int ready = 0;
      while(!ready) {
        ready = select(fd+1, &fds, 0, 0, 0);
      }

      state->vm()->thread->sleep(state, cFalse);
      state->vm()->clear_waiter();
    }
    ENTER_CAPI(env->state());
  }
Exemple #10
0
  /** @note   Shares code with rb_define_class_under, change there too. --rue */
  VALUE rb_define_module_under(VALUE parent_handle, const char* name) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Module* parent = c_as<Module>(env->get_object(parent_handle));
    Symbol* constant = env->state()->symbol(name);

    LEAVE_CAPI(env->state());
    Module* module = rubinius::Helpers::open_module(env->state(),
        env->current_call_frame(), parent, constant);

    // The call above could have triggered an Autoload resolve, which may
    // raise an exception, so we have to check the value returned.
    if(!module) env->current_ep()->return_to(env);

    // Grab the module handle before grabbing the lock
    // so the Module isn't accidentally GC'ed.
    VALUE module_handle = env->get_handle(module);
    ENTER_CAPI(env->state());

    return module_handle;
  }
Exemple #11
0
  int rb_thread_select(int max, fd_set* read, fd_set* write, fd_set* except,
                       struct timeval *input_tv)
  {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    int ret = 0;

    struct timeval tv;
    struct timeval absolute_tv;
    struct timeval* tvp = NULL;

    if(input_tv) {
      // We make a new timeval rather than using input_tv because we modify it.
      tv = *input_tv;
      tvp = &tv;

      gettimeofday(&absolute_tv, NULL);

      timeradd(&absolute_tv, &tv, &absolute_tv);
    }

    for(;;) {
      {
        GlobalLock::UnlockGuard guard(env);
        ret = select(max, read, write, except, tvp);
      }

      if(!env->state()->check_async(env->current_call_frame())) {
        // Ok, there was an exception raised by an async event. We need
        // to unwind through the caller back the entrance to the native
        // method.

        // Only handle true exceptions being raised, eat all other requests
        // for now.

        if(env->state()->thread_state()->raise_reason() == cException) {
          capi::capi_raise_backend(env->state()->thread_state()->current_exception());
        } else {
          env->state()->thread_state()->clear();
        }
      }

      if(ret < 0 && errno == EINTR) {
        if(input_tv) {
          struct timeval cur_tv;
          gettimeofday(&cur_tv, NULL);
          timersub(&absolute_tv, &cur_tv, &tv);
        }
      } else {
        break;
      }
    }

    return ret;
  }
Exemple #12
0
  /** @note   Shares code with rb_define_class_under, change there too. --rue */
  VALUE rb_define_module_under(VALUE parent_handle, const char* name) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Module* parent = c_as<Module>(env->get_object(parent_handle));
    Symbol* constant = env->state()->symbol(name);

    Module* module = rubinius::Helpers::open_module(env->state(),
        env->current_call_frame(), parent, constant);

    return env->get_handle(module);
  }
Exemple #13
0
  VALUE rb_const_get(VALUE module_handle, ID id_name) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Symbol* name = reinterpret_cast<Symbol*>(id_name);
    Module* module = c_as<Module>(env->get_object(module_handle));

    bool found = false;
    while(!module->nil_p()) {
      Object* val = module->get_const(env->state(), name, &found);
      if(found) {
        if(Autoload* autoload = try_as<Autoload>(val)) {
          return capi_fast_call(env->get_handle(autoload), rb_intern("call"), 0);
        }

        return env->get_handle(val);
      }

      module = module->superclass();
    }

    // Try from Object as well.
    module = env->state()->globals().object.get();

    while(!module->nil_p()) {
      Object* val = module->get_const(env->state(), name, &found);
      if(found) {
        if(Autoload* autoload = try_as<Autoload>(val)) {
          return capi_fast_call(env->get_handle(autoload), rb_intern("call"), 0);
        }

        return env->get_handle(val);
      }

      module = module->superclass();
    }

    return const_missing(module_handle, id_name);
  }
Exemple #14
0
  void capi_define_method(const char* file, VALUE target,
                          const char* name, CApiGenericFunction fptr,
                          int arity, CApiMethodKind kind)
  {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    State* state = env->state();
    Symbol* method_name = state->symbol(name);

    Module* module = NULL;

    if(kind == cCApiSingletonMethod) {
      module = c_as<Module>(env->get_object(target)->singleton_class(env->state()));
    } else {
      module = c_as<Module>(env->get_object(target));
    }

    NativeMethod* method = NULL;
    method = NativeMethod::create(state, String::create(state, file),
                                  module, method_name,
                                  (void*)fptr, Fixnum::from(arity),
                                  env->current_native_frame()->capi_lock_index());

    Symbol* visibility;

    switch(kind) {
    case cCApiPrivateMethod:
      visibility = G(sym_private);
      break;

    case cCApiProtectedMethod:
      visibility = G(sym_protected);
      break;

    default:  /* Also catches singletons for now. @todo Verify OK. --rue */
      visibility = G(sym_public);
      break;
    }

    module->add_method(state, method_name, method, visibility);
    System::vm_reset_method_cache(env->state(), module, method_name, env->current_call_frame());
  }
Exemple #15
0
  VALUE rb_block_call(VALUE obj, ID meth, int argc, VALUE* argv,
                      VALUE(*cb)(ANYARGS), VALUE cb_data) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    if(cb) {
      Proc* prc = capi::wrap_c_function((void*)cb, cb_data, C_BLOCK_CALL);
      env->set_outgoing_block(env->get_handle(prc));
    } else {
      env->set_outgoing_block(env->get_handle(env->block()));
    }

    return rb_funcall2(obj, meth, argc, argv);
  }
Exemple #16
0
  /** @note   Shares code with rb_define_module_under, change there too. --rue */
  VALUE rb_define_class_id_under(VALUE outer, ID name, VALUE super) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Module* module = c_as<Module>(env->get_object(outer));
    Class* superclass = c_as<Class>(env->get_object(super ? super : rb_cObject));
    Symbol* constant = reinterpret_cast<Symbol*>(name);

    bool created = false;

    LEAVE_CAPI(env->state());
    Class* opened_class = NULL;

    // Run in a block so OnStack is properly deallocated before we
    // might do a longjmp because of an exception.
    {
      OnStack<3> os(env->state(), module, superclass, constant);

      opened_class = rubinius::Helpers::open_class(env->state(),
          module, superclass, constant, &created);
    }

    // The call above could have triggered an Autoload resolve, which may
    // raise an exception, so we have to check the value returned.
    if(!opened_class) env->current_ep()->return_to(env);

    // We need to grab the handle before entering back into C-API
    // code. The problem otherwise can be that the GC runs and
    // the opened_class is GC'ed.

    VALUE klass = env->get_handle(opened_class);
    ENTER_CAPI(env->state());

    if(super) rb_funcall(super, rb_intern("inherited"), 1, klass);

    return klass;
  }
Exemple #17
0
  VALUE rb_path2class(const char* path) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    State* state = env->state();
    Module* mod = G(object);

    char* pathd = strdup(path);
    char* ptr = pathd;
    char* context;
    char* name;

    ConstantMissingReason reason = vNonExistent;

    while((name = strtok_r(ptr, ":", &context))) {
      ptr = NULL;

      Object* val = mod->get_const(env->state(), env->state()->symbol(name), G(sym_private), &reason);

      if(reason != vFound) {
        free(pathd);
        rb_raise(rb_eArgError, "undefined class or module %s", path);
      }

      if(Autoload* autoload = try_as<Autoload>(val)) {
        VALUE m = capi_fast_call(env->get_handle(autoload),
            rb_intern("call"), 1, env->get_handle(mod));
        val = env->get_object(m);
      }

      if(!(mod = try_as<Module>(val))) {
        free(pathd);
        capi_raise_type_error(Module::type, val);
      }
    }

    free(pathd);

    return env->get_handle(mod);
  }
Exemple #18
0
  VALUE rb_rescue2(VALUE (*func)(ANYARGS), VALUE arg1,
                   VALUE (*raise_func)(ANYARGS), VALUE arg2, ...)
  {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    VALUE ret = Qnil;
    ExceptionPoint ep(env);
    va_list exc_classes;

    PLACE_EXCEPTION_POINT(ep);
    if(unlikely(ep.jumped_to())) {
      ep.pop(env);

      if(env->state()->thread_state()->raise_reason() != cException) {
        //Something bad happened, we shouldn't be here.
        return Qnil;
      }

      VALUE exc_handle = env->get_handle(env->state()->thread_state()->as_object(env->state()));
      bool handle_exc = false;

      va_start(exc_classes, arg2);
      while(VALUE eclass = va_arg(exc_classes, VALUE)) {
        if(rb_obj_is_kind_of(exc_handle, eclass) == Qtrue) {
          handle_exc = true;
          break;
        }
      }
      va_end(exc_classes);

      if(handle_exc) {
        ret = (*raise_func)(arg2);
        env->state()->thread_state()->clear_exception();
      } else {
        env->current_ep()->return_to(env);
      }

    } else {
      ret = (*func)(arg1);
      ep.pop(env);
    }

    return ret;
  }
Exemple #19
0
  VALUE rb_data_object_alloc(VALUE klass, void* ptr,
      RUBY_DATA_FUNC mark, RUBY_DATA_FUNC free) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Class* data_klass = c_as<Class>(env->get_object(klass));

    Data* data = Data::create(env->state(), ptr, mark, free);

    data->klass(env->state(), data_klass);

    // Data objects are directly manipulated, so we have to always
    // track them.
    env->state()->om->remember_object(data);

    return env->get_handle(data);
  }
Exemple #20
0
  VALUE rb_enc_associate_index(VALUE obj, int index) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Encoding* enc = as<Encoding>(
        Encoding::encoding_list(env->state())->get(env->state(), index));

    Object* val = env->get_object(obj);

    if(String* str = try_as<String>(val)) {
      str->encoding(env->state(), enc);
    } else if(Regexp* reg = try_as<Regexp>(val)) {
      reg->encoding(env->state(), enc);
    } else if(Symbol* sym = try_as<Symbol>(val)) {
      sym->encoding(env->state(), enc);
    } else {
      rb_raise(rb_eArgError, "object does not have an associated Encoding");
    }

    return obj;
  }
Exemple #21
0
  VALUE rb_gv_get(const char* name) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    long len;

    len = strlen(name);
    if((len == 1 && name[0] == '~') ||
       (len == 2 && name[0] == '$' && name[1] == '~')) {
      return env->get_handle(Regexp::last_match_result(env->state(),
        Fixnum::from(0), Fixnum::from(0), env->current_call_frame()));
    }
    VALUE Globals = rb_const_get(rb_mRubinius, rb_intern("Globals"));

    return rb_funcall(Globals,
                      rb_intern("[]"),
                      1,
                      env->get_handle(prefixed_by(env->state(), '$', rb_intern(name))));
  }
Exemple #22
0
  VALUE rb_ary_new4(unsigned long length, const VALUE* objects) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Array* array = Array::create(env->state(), length);
    array->start(env->state(), Fixnum::from(0));
    array->total(env->state(), Fixnum::from(length));

    if(objects) {
      for(std::size_t i = 0; i < length; ++i) {
        // @todo determine if we need to check these objects for whether
        // they are arrays and flush any caches
        Object* object = env->get_object(objects[i]);
        array->set(env->state(), i, object);
      }
    }

    return env->get_handle(array);
  }
Exemple #23
0
  VALUE rb_time_timespec_new(const struct timespec *ts, int offset) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Time* time = Time::at(env->state(), ts->tv_sec, ts->tv_nsec);

    if(-86400 < offset && offset < 86400) { /* fixed offset */
      time->offset(env->state(), Fixnum::from(offset));
      time->is_gmt(env->state(), cFalse);
      time->zone(env->state(), cNil);
    } else if(offset == INT_MAX) { /* localtime */
    } else if(offset == INT_MAX - 1) { /* UTC */
      time->is_gmt(env->state(), cTrue);
    } else {
      rb_raise(rb_eArgError, "utc_offset out of range");
    }

    return env->get_handle(time);
  }
Exemple #24
0
 VALUE rb_int_positive_pow(long x, unsigned long y) {
   NativeMethodEnvironment* env = NativeMethodEnvironment::get();
   State* state = env->state();
   Integer* base = Integer::from(state, x);
   Integer* exp  = Integer::from(state, y);
   if(Fixnum* base_fix = try_as<Fixnum>(base)) {
     if(Fixnum* exp_fix = try_as<Fixnum>(exp)) {
       return env->get_handle(base_fix->pow(state, exp_fix));
     } else {
       return env->get_handle(base_fix->pow(state, as<Bignum>(exp)));
     }
   } else {
      if(Fixnum* exp_fix = try_as<Fixnum>(exp)) {
       return env->get_handle(as<Bignum>(base)->pow(state, exp_fix));
     } else {
       return env->get_handle(as<Bignum>(base)->pow(state, as<Bignum>(exp)));
     }
   }
 }
Exemple #25
0
  int rb_to_encoding_index(VALUE obj) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    Encoding* enc;

    switch(TYPE(obj)) {
    case T_ENCODING:
      enc = c_as<Encoding>(env->get_object(obj));
      break;
    case T_STRING:
      enc = Encoding::find(env->state(), RSTRING_PTR(obj));
      break;
    default:
      obj = rb_funcall(obj, rb_intern("to_str"), 0);
      enc = Encoding::find(env->state(), RSTRING_PTR(obj));
    }

    if(enc->nil_p()) return -1;

    return Encoding::find_index(env->state(), enc->name()->c_str(env->state()));
  }
Exemple #26
0
  VALUE rb_ary_to_ary(VALUE object) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Object* obj = env->get_object(object);

    if(kind_of<Array>(obj)) {
      return object;
    }

    ID to_ary_id = rb_intern("to_ary");

    if(rb_respond_to(object, to_ary_id)) {
      return rb_funcall(object, to_ary_id, 0);
    } else {
      Array* array = Array::create(env->state(), 1);
      array->set(env->state(), 0, env->get_object(object));

      return env->get_handle(array);
    }
  }
Exemple #27
0
  int rb_enc_get_index(VALUE obj) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Object* val = env->get_object(obj);
    Encoding* enc;

    if(String* str = try_as<String>(val)) {
      enc = str->encoding(env->state());
    } else if(Regexp* reg = try_as<Regexp>(val)) {
      enc = reg->encoding(env->state());
    } else if(Symbol* sym = try_as<Symbol>(val)) {
      enc = sym->encoding(env->state());
    } else {
      rb_raise(rb_eArgError, "object does not have an associated Encoding");
    }

    if(enc->nil_p()) return -1;

    return Encoding::find_index(env->state(), enc->get_encoding()->name);
  }
Exemple #28
0
  VALUE rb_ary_new3(unsigned long length, ...) {
    va_list args;
    va_start(args, length);

    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Array* array = Array::create(env->state(), length);
    array->start(env->state(), Fixnum::from(0));
    array->total(env->state(), Fixnum::from(length));

    for(unsigned long i = 0; i < length; ++i) {
      // @todo determine if we need to check these objects for whether
      // they are arrays and flush any caches
      Object* object = env->get_object(va_arg(args, VALUE));
      array->set(env->state(), i, object);
    }

    va_end(args);

    return env->get_handle(array);
  }
Exemple #29
0
  VALUE rb_funcall2(VALUE receiver, ID method_name, int arg_count, const VALUE* v_args) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Object* args[arg_count];

    for(int i = 0; i < arg_count; i++) {
      args[i] = env->get_object(v_args[i]);
    }

    Object* blk = cNil;

    if(VALUE blk_handle = env->outgoing_block()) {
      blk = env->get_object(blk_handle);
      env->set_outgoing_block(0);
    }

    return capi_funcall_backend_native(env, "", 0,
        env->get_object(receiver),
        reinterpret_cast<Symbol*>(method_name),
        arg_count, args, blk);
  }
Exemple #30
0
  // THAR BE DRAGONS.
  //
  // When venturing through the valleys of the unmanaged, our hero must
  // remain vigilant and disiplined! If she should ever find a VALUE for
  // a reference in her travels: Look away! For these VALUEs are pure
  // death! Our hero must steel herself and continue on her quest, returning
  // as soon as possible to the castle of the managed.
  VALUE rb_thread_blocking_region(rb_blocking_function_t func, void* data,
                                  rb_unblock_function_t ubf, void* ubf_data) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    State* state = env->state();
    VALUE ret = Qnil;

    if (ubf == RUBY_UBF_IO || ubf == RUBY_UBF_PROCESS) {
      state->vm()->interrupt_with_signal();
    } else {
      state->vm()->wait_on_custom_function(ubf, ubf_data);
    }
    env->state()->shared().leave_capi(env->state());
    {
      GCIndependent guard(env);
      ret = (*func)(data);
    }
    env->state()->shared().enter_capi(env->state());
    state->vm()->clear_waiter();

    return ret;
  }