ext/pycall/ruby_wrapper.c in pycall-1.0.3 vs ext/pycall/ruby_wrapper.c in pycall-1.1.0.rc1

- old
+ new

@@ -3,33 +3,34 @@ static PyMemberDef PyRuby_members[] = { {"ruby_object_ptr", Py_T_PYSSIZET, offsetof(PyRubyObject, ruby_object), Py_READONLY}, {NULL} /* sentinel */ }; -static void PyRuby_dealloc(PyRubyObject *); -static PyObject * PyRuby_repr(PyRubyObject *); -static PyObject * PyRuby_call(PyRubyObject *, PyObject *, PyObject *); -static PyObject * PyRuby_getattro(PyRubyObject *, PyObject *); +static VALUE PyRuby_get_ruby_object_and_set_pyerr(PyObject *pyobj); +static void PyRuby_dealloc_with_gvl(PyRubyObject *); +static PyObject * PyRuby_repr_with_gvl(PyRubyObject *); +static PyObject * PyRuby_call_with_gvl(PyRubyObject *, PyObject *, PyObject *); +static PyObject * PyRuby_getattro_with_gvl(PyRubyObject *, PyObject *); PyTypeObject PyRuby_Type = { PyVarObject_HEAD_INIT(NULL, 0) "PyCall.ruby_object", /* tp_name */ sizeof(PyRubyObject), /* tp_basicsize */ 0, /* tp_itemsize */ - (destructor)PyRuby_dealloc, /* tp_dealloc */ + (destructor)PyRuby_dealloc_with_gvl, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ - (reprfunc)PyRuby_repr, /* tp_repr */ + (reprfunc)PyRuby_repr_with_gvl, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ {0}, /* tp_hash */ - (ternaryfunc)PyRuby_call, /* tp_call */ + (ternaryfunc)PyRuby_call_with_gvl, /* tp_call */ 0, /* tp_str */ - (getattrofunc)PyRuby_getattro, /* tp_getattro */ + (getattrofunc)PyRuby_getattro_with_gvl, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_BASETYPE, /* tp_flags */ "ruby object wrapper", /* tp_doc */ 0, /*tp_traverse*/ @@ -40,74 +41,32 @@ 0, /*tp_iternext*/ 0, /*tp_methods*/ PyRuby_members, /*tp_members*/ }; -PyObject * -PyRuby_New(VALUE ruby_object) +static PyObject * +PyRuby_New_impl(VALUE ruby_object) { PyRubyObject *op; op = (PyRubyObject *)Py_API(_PyObject_New)(&PyRuby_Type); op->ruby_object = ruby_object; pycall_gcguard_register_pyrubyobj((PyObject *)op); return (PyObject *)op; } -static VALUE -funcall_id2ref(VALUE object_id) +PyObject * +PyRuby_New(VALUE ruby_object) { - VALUE rb_mObjSpace; - object_id = rb_check_to_integer(object_id, "to_int"); - rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace")); - return rb_funcall(rb_mObjSpace, rb_intern("_id2ref"), 1, object_id); -} - -static VALUE -protect_id2ref(VALUE object_id) -{ - VALUE obj; - int state; - - obj = rb_protect((VALUE (*)(VALUE))funcall_id2ref, object_id, &state); - if (state) - return Qundef; - - return obj; -} - -static VALUE -protect_id2ref_and_set_pyerr(VALUE object_id) -{ - VALUE obj = protect_id2ref(object_id); - if (obj != Qundef) - return obj; - - obj = rb_errinfo(); - if (RTEST(rb_obj_is_kind_of(obj, rb_eRangeError))) { - Py_API(PyErr_SetString)(Py_API(PyExc_RuntimeError), "[BUG] referenced object was garbage-collected"); + if (!ruby_thread_has_gvl_p()) { + CALL_WITH_GVL(PyRuby_New_impl, ruby_object); } - else { - VALUE emesg = rb_check_funcall(obj, rb_intern("message"), 0, 0); - Py_API(PyErr_Format)(Py_API(PyExc_RuntimeError), - "[BUG] Unable to obtain ruby object from ID: %s (%s)", - StringValueCStr(emesg), rb_class2name(CLASS_OF(obj))); - } - return Qundef; -} -static VALUE -PyRuby_get_ruby_object_and_set_pyerr(PyObject *pyobj) -{ - VALUE obj_id; - if (!PyRuby_Check(pyobj)) - return Qundef; - obj_id = rb_obj_id(PyRuby_get_ruby_object(pyobj)); - return protect_id2ref_and_set_pyerr(obj_id); + return PyRuby_New_impl(ruby_object); } -static void +static void * PyRuby_dealloc(PyRubyObject *pyro) { VALUE obj; obj = PyRuby_get_ruby_object((PyObject *)pyro); @@ -115,15 +74,26 @@ #ifdef PYCALL_DEBUG_DUMP_REFCNT fprintf(stderr, "PyRuby_dealloc(%p), ruby_object=%"PRI_LL_PREFIX"d\n", pyro, NUM2LL(rb_obj_id(obj))); #endif /* PYCALL_DEBUG_DUMP_REFCNT */ if (obj == Qundef) - return; + return NULL; pycall_gcguard_unregister_pyrubyobj((PyObject *)pyro); + + return NULL; } +static void +PyRuby_dealloc_with_gvl(PyRubyObject *pyro) +{ + if (!ruby_thread_has_gvl_p()) { + CALL_WITH_GVL(PyRuby_dealloc, pyro); + } + PyRuby_dealloc(pyro); +} + static PyObject * PyRuby_repr(PyRubyObject *pyro) { VALUE obj, str; PyObject *res; @@ -135,10 +105,19 @@ str = rb_inspect(obj); res = pycall_pystring_from_format("<PyCall.ruby_object %s>", StringValueCStr(str)); return res; } +static PyObject * +PyRuby_repr_with_gvl(PyRubyObject *pyro) +{ + if (!ruby_thread_has_gvl_p()) { + return CALL_WITH_GVL(PyRuby_repr, pyro); + } + return PyRuby_repr(pyro); +} + #if SIZEOF_SSIZE_T < 8 /* int64to32hash from src/support/hashing.c in Julia */ static inline uint32_t int64to32hash(uint64_t key) { @@ -150,48 +129,66 @@ key = key ^ (key >> 22); return (uint32_t)key; } #endif -static long +static void * PyRuby_hash_long(PyRubyObject *pyro) { VALUE obj, rbhash; long h; obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro); if (obj == Qundef) - return -1; + return (void *)-1; rbhash = rb_hash(obj); h = FIX2LONG(rbhash); /* Ruby's hash value is a Fixnum */ - return h == -1 ? pycall_hash_salt : h; + return (void *)(h == -1 ? pycall_hash_salt : h); } -static Py_hash_t +static long +PyRuby_hash_long_with_gvl(PyRubyObject *pyro) +{ + if (!ruby_thread_has_gvl_p()) { + return (long)CALL_WITH_GVL(PyRuby_hash_long, pyro); + } + return (long)PyRuby_hash_long(pyro); +} + +static void * PyRuby_hash_hash_t(PyRubyObject *pyro) { VALUE obj, rbhash; Py_hash_t h; obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro); if (obj == Qundef) - return -1; + return (void *)-1; rbhash = rb_hash(obj); #if SIZEOF_PY_HASH_T == SIZEOF_LONG /* In this case, we can assume sizeof(Py_hash_t) == sizeof(long) */ h = NUM2SSIZET(rbhash); - return h == -1 ? (Py_hash_t)pycall_hash_salt : h; + return (void *)(h == -1 ? (Py_hash_t)pycall_hash_salt : h); #else /* In this case, we can assume sizeof(long) == 4 and sizeof(Py_hash_t) == 8 */ h = (pycall_hash_salt_32 << 32) | FIX2LONG(rbhash); - return h == -1 ? ((pycall_hash_salt << 32) | pycall_hash_salt) : h; + return (void *)(h == -1 ? ((pycall_hash_salt << 32) | pycall_hash_salt) : h); #endif } +static Py_hash_t +PyRuby_hash_hash_t_with_gvl(PyRubyObject *pyro) +{ + if (!ruby_thread_has_gvl_p()) { + return (Py_hash_t)CALL_WITH_GVL(PyRuby_hash_hash_t, pyro); + } + return (Py_hash_t)PyRuby_hash_hash_t(pyro); +} + struct call_rb_funcallv_params { VALUE recv; ID meth; int argc; VALUE *argv; @@ -219,31 +216,37 @@ if (pstate) *pstate = state; if (state) return Qnil; return res; } +struct PyRuby_call_params { + PyRubyObject *pyro; + PyObject *pyobj_args; + PyObject *pyobj_kwargs; +}; + static PyObject * -PyRuby_call(PyRubyObject *pyro, PyObject *pyobj_args, PyObject *pyobj_kwargs) +PyRuby_call(struct PyRuby_call_params *params) { ID id_call; VALUE obj, args, kwargs, res; PyObject *pyobj_res; int state; - obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro); + obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)params->pyro); if (obj == Qundef) return NULL; id_call = rb_intern("call"); if (!rb_respond_to(obj, id_call)) { Py_API(PyErr_SetString)(Py_API(PyExc_TypeError), "non-callable ruby object"); return NULL; } - args = pycall_pyobject_to_a(pyobj_args); - if (pyobj_kwargs) { - kwargs = pycall_pyobject_to_ruby(pyobj_kwargs); + args = pycall_pyobject_to_a(params->pyobj_args); + if (params->pyobj_kwargs) { + kwargs = pycall_pyobject_to_ruby(params->pyobj_kwargs); rb_ary_push(args, kwargs); } res = rb_protect_funcallv(obj, id_call, (int)RARRAY_LEN(args), RARRAY_PTR(args), &state); if (state) { @@ -253,22 +256,42 @@ pyobj_res = pycall_pyobject_from_ruby(res); return pyobj_res; } static PyObject * -PyRuby_getattro(PyRubyObject *pyro, PyObject *pyobj_name) +PyRuby_call_with_gvl(PyRubyObject *pyro, PyObject *pyobj_args, PyObject *pyobj_kwargs) { + struct PyRuby_call_params params; + params.pyro = pyro; + params.pyobj_args = pyobj_args; + params.pyobj_kwargs = pyobj_kwargs; + + if (!ruby_thread_has_gvl_p()) { + return CALL_WITH_GVL(PyRuby_call, &params); + } + + return PyRuby_call(&params); +} + +struct PyRuby_getattro_params { + PyRubyObject *pyro; + PyObject *pyobj_name; +}; + +static PyObject * +PyRuby_getattro(struct PyRuby_getattro_params *params) +{ VALUE obj, name, res; char const *name_cstr; ID name_id; PyObject *pyobj_res; - obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro); + obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)params->pyro); if (obj == Qundef) return NULL; - name = pycall_pyobject_to_ruby(pyobj_name); + name = pycall_pyobject_to_ruby(params->pyobj_name); name_cstr = StringValueCStr(name); name_id = rb_intern(name_cstr); /* TODO: should handle exception */ if (strncmp(name_cstr, "__name__", 8) == 0 || @@ -297,25 +320,39 @@ res = Qnil; } else if (name_cstr[0] == '_' && name_cstr[1] == '_') { /* name.start_with? "__" */ /* TODO: handle `__code__` and `func_code` */ - return Py_API(PyObject_GenericGetAttr)((PyObject *)pyro, pyobj_name); + return Py_API(PyObject_GenericGetAttr)((PyObject *)params->pyro, params->pyobj_name); } else { /* TODO: handle `__code__` and `func_code` */ if (rb_respond_to(obj, name_id)) { VALUE method = rb_obj_method(obj, name); - return PyRuby_New(method); + return PyRuby_New_impl(method); } - return Py_API(PyObject_GenericGetAttr)((PyObject *)pyro, pyobj_name); + return Py_API(PyObject_GenericGetAttr)((PyObject *)params->pyro, params->pyobj_name); } pyobj_res = pycall_pyobject_from_ruby(res); return pyobj_res; } +static PyObject * +PyRuby_getattro_with_gvl(PyRubyObject *pyro, PyObject *pyobj_name) +{ + struct PyRuby_getattro_params params; + params.pyro = pyro; + params.pyobj_name = pyobj_name; + + if (!ruby_thread_has_gvl_p()) { + return CALL_WITH_GVL(PyRuby_getattro, &params); + } + + return PyRuby_getattro(&params); +} + /* ==== PyCall::PyRubyPtr ==== */ VALUE cPyRubyPtr; const rb_data_type_t pycall_pyrubyptr_data_type = { @@ -409,13 +446,13 @@ { PyRuby_Type.ob_base.ob_type = Py_API(PyType_Type); PyRuby_Type.tp_flags |= pycall_default_tp_flags(); PyRuby_Type.tp_new = Py_API(PyType_GenericNew); if (pycall_python_long_hash) - PyRuby_Type.tp_hash._long = (hashfunc_long)PyRuby_hash_long; + PyRuby_Type.tp_hash._long = (hashfunc_long)PyRuby_hash_long_with_gvl; else - PyRuby_Type.tp_hash._hash_t = (hashfunc_hash_t)PyRuby_hash_hash_t; + PyRuby_Type.tp_hash._hash_t = (hashfunc_hash_t)PyRuby_hash_hash_t_with_gvl; if (Py_API(PyType_Ready)(&PyRuby_Type) < 0) { pycall_pyerror_fetch_and_raise("PyType_Ready in pycall_init_ruby_wrapper"); } Py_API(Py_IncRef)((PyObject *)&PyRuby_Type); @@ -427,6 +464,60 @@ cPyRubyPtr = rb_define_class_under(mPyCall, "PyRubyPtr", cPyPtr); rb_define_alloc_func(cPyRubyPtr, pycall_pyruby_allocate); rb_define_method(cPyRubyPtr, "__ruby_object_id__", pycall_pyruby_get_ruby_object_id, 0); rb_define_module_function(mPyCall, "wrap_ruby_object", pycall_m_wrap_ruby_object, 1); +} + +/* --- File internal utilities --- */ + +static VALUE +funcall_id2ref(VALUE object_id) +{ + VALUE rb_mObjSpace; + object_id = rb_check_to_integer(object_id, "to_int"); + rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace")); + return rb_funcall(rb_mObjSpace, rb_intern("_id2ref"), 1, object_id); +} + +static VALUE +protect_id2ref(VALUE object_id) +{ + VALUE obj; + int state; + + obj = rb_protect((VALUE (*)(VALUE))funcall_id2ref, object_id, &state); + if (state) + return Qundef; + + return obj; +} + +static VALUE +protect_id2ref_and_set_pyerr(VALUE object_id) +{ + VALUE obj = protect_id2ref(object_id); + if (obj != Qundef) + return obj; + + obj = rb_errinfo(); + if (RTEST(rb_obj_is_kind_of(obj, rb_eRangeError))) { + Py_API(PyErr_SetString)(Py_API(PyExc_RuntimeError), "[BUG] referenced object was garbage-collected"); + } + else { + VALUE emesg = rb_check_funcall(obj, rb_intern("message"), 0, 0); + Py_API(PyErr_Format)(Py_API(PyExc_RuntimeError), + "[BUG] Unable to obtain ruby object from ID: %s (%s)", + StringValueCStr(emesg), rb_class2name(CLASS_OF(obj))); + } + return Qundef; +} + +static VALUE +PyRuby_get_ruby_object_and_set_pyerr(PyObject *pyobj) +{ + VALUE obj_id; + if (!PyRuby_Check(pyobj)) + return Qundef; + obj_id = rb_obj_id(PyRuby_get_ruby_object(pyobj)); + return protect_id2ref_and_set_pyerr(obj_id); }