From d23934feab3d4249a491f0e1c882060397b43fea Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 8 Sep 2014 11:31:47 +0200 Subject: [PATCH] Improve Python reference counting. --- .../handwritten_definitions.mustache | 8 +- tools/python/apixml2python/linphone.py | 86 ++++++++++--------- .../apixml2python/linphone_module.mustache | 12 ++- 3 files changed, 56 insertions(+), 50 deletions(-) diff --git a/tools/python/apixml2python/handwritten_definitions.mustache b/tools/python/apixml2python/handwritten_definitions.mustache index 46457beb2..ae6c2f84a 100644 --- a/tools/python/apixml2python/handwritten_definitions.mustache +++ b/tools/python/apixml2python/handwritten_definitions.mustache @@ -259,18 +259,16 @@ static PyObject * pylinphone_Core_class_method_new_with_config(PyObject *cls, Py static void pylinphone_ChatRoom_callback_chat_message_state_changed(LinphoneChatMessage *msg, LinphoneChatMessageState state, void *ud) { PyGILState_STATE pygil_state; PyObject *pycm = NULL; + bool_t incref = FALSE; PyObject *_dict = (PyObject *)ud; PyObject *_cb = PyDict_GetItemString(_dict, "callback"); PyObject *_ud = PyDict_GetItemString(_dict, "user_data"); pygil_state = PyGILState_Ensure(); - pycm = linphone_chat_message_get_user_data(msg); - if (pycm == NULL) { - pycm = pylinphone_ChatMessage_new_from_native_ptr(&pylinphone_ChatMessageType, msg); - } + pycm = pylinphone_ChatMessage_from_native_ptr(&pylinphone_ChatMessageType, msg, &incref); pylinphone_trace(1, "[PYLINPHONE] >>> %s(%p, %p [%p], %d, %p)", __FUNCTION__, pycm, msg, state, ud); if ((_cb != NULL) && PyCallable_Check(_cb)) { - if (PyEval_CallObject(_cb, Py_BuildValue("OiO", pycm, state, _ud)) == NULL) { + if (PyEval_CallObject(_cb, Py_BuildValue((incref == TRUE) ? "OiO" : "NiO", pycm, state, _ud)) == NULL) { PyErr_Print(); } } diff --git a/tools/python/apixml2python/linphone.py b/tools/python/apixml2python/linphone.py index 0825d8d62..ac9577124 100644 --- a/tools/python/apixml2python/linphone.py +++ b/tools/python/apixml2python/linphone.py @@ -208,7 +208,9 @@ class MethodDefinition: self.build_value_format = argument_type.fmt_str if self.build_value_format == 'O': body += "\tPyObject * pyresult;\n" + body += "\tbool_t incref = FALSE;\n" body += "\tPyObject * pyret;\n" + body += "\tconst char *pyret_fmt;\n" if self.self_arg is not None: body += "\t" + self.self_arg.get('completetype') + "native_ptr;\n" for xml_method_arg in self.xml_method_args: @@ -315,9 +317,9 @@ class MethodDefinition: if len(arg_names) > 0: c_function_call_code += ', ' c_function_call_code += ', '.join(arg_names) + ");" - return_from_user_data_code = '' - new_from_native_pointer_code = '' + from_native_pointer_code = '' convert_from_code = '' + pyret_fmt_fill_code = '' build_value_code = '' result_variable = '' if self.return_complete_type != 'void': @@ -325,36 +327,31 @@ class MethodDefinition: stripped_return_type = strip_leading_linphone(self.return_type) return_type_class = self.find_class_definition(self.return_type) if return_type_class is not None: - if return_type_class['class_has_user_data']: - get_user_data_function = return_type_class['class_c_function_prefix'] + "get_user_data" - return_from_user_data_code = \ -"""if ((cresult != NULL) && ({func}(cresult) != NULL)) {{ - return Py_BuildValue("O", (PyObject *){func}(cresult)); - }} -""".format(func=get_user_data_function) - new_from_native_pointer_code = "pyresult = pylinphone_{return_type}_new_from_native_ptr(&pylinphone_{return_type}Type, cresult);\n".format(return_type=stripped_return_type) + from_native_pointer_code = "pyresult = pylinphone_{return_type}_from_native_ptr(&pylinphone_{return_type}Type, cresult, &incref);\n".format(return_type=stripped_return_type) else: return_argument_type = ArgumentType(self.return_type, self.return_complete_type, self.return_contained_type, self.linphone_module) if return_argument_type.convert_from_func is not None: convert_from_code = \ """pyresult = {convert_func}(cresult); """.format(convert_func=return_argument_type.convert_from_func) + pyret_fmt_fill_code = "pyret_fmt = (incref == TRUE) ? \"O\" : \"N\";" result_variable = 'pyresult' else: + pyret_fmt_fill_code = "pyret_fmt = \"{fmt}\";\n".format(fmt=self.build_value_format) result_variable = 'cresult' if result_variable != '': - build_value_code = "pyret = Py_BuildValue(\"{fmt}\", {result_variable});\n".format(fmt=self.build_value_format.replace('O', 'N'), result_variable=result_variable) + build_value_code = "pyret = Py_BuildValue(pyret_fmt, {result_variable});".format(fmt=self.build_value_format.replace('O', 'N'), result_variable=result_variable) body = \ """ {c_function_call_code} pylinphone_dispatch_messages(); - {return_from_user_data_code} - {new_from_native_pointer_code} + {from_native_pointer_code} {convert_from_code} + {pyret_fmt_fill_code} {build_value_code} """.format(c_function_call_code=c_function_call_code, - return_from_user_data_code=return_from_user_data_code, - new_from_native_pointer_code=new_from_native_pointer_code, + from_native_pointer_code=from_native_pointer_code, convert_from_code=convert_from_code, + pyret_fmt_fill_code=pyret_fmt_fill_code, build_value_code=build_value_code) return body @@ -510,12 +507,12 @@ class InitMethodDefinition(MethodDefinition): def format_return_result(self): return "\treturn 0;" -class NewFromNativePointerMethodDefinition(MethodDefinition): +class FromNativePointerMethodDefinition(MethodDefinition): def __init__(self, linphone_module, class_): MethodDefinition.__init__(self, linphone_module, class_, None) def format_local_variables_definition(self): - return "\tpylinphone_{class_name}Object *self;\n".format(class_name=self.class_['class_name']) + return "\tpylinphone_{class_name}Object *self = NULL;\n".format(class_name=self.class_['class_name']) def format_arguments_parsing(self): return '' @@ -524,28 +521,38 @@ class NewFromNativePointerMethodDefinition(MethodDefinition): return "\tpylinphone_trace(1, \"[PYLINPHONE] >>> %s(%p)\", __FUNCTION__, native_ptr);\n" def format_c_function_call(self): + get_user_data_func_call = '' set_user_data_func_call = '' + incref_code = '' if self.class_['class_has_user_data']: + get_user_data_func_call = "self = (pylinphone_{class_name}Object *){function_prefix}get_user_data(native_ptr);".format(class_name=self.class_['class_name'], function_prefix=self.class_['class_c_function_prefix']) set_user_data_func_call = "{function_prefix}set_user_data(self->native_ptr, self);".format(function_prefix=self.class_['class_c_function_prefix']) + incref_code = "*incref = TRUE;" ref_native_pointer_code = '' if self.class_['class_refcountable']: ref_native_pointer_code = "{func}(self->native_ptr);".format(func=self.class_['class_c_function_prefix'] + "ref") return \ -""" if (native_ptr == NULL) {{ +""" *incref = FALSE; + if (native_ptr == NULL) {{ {none_trace} Py_RETURN_NONE; }} - self = (pylinphone_{class_name}Object *)PyObject_CallObject((PyObject *)&pylinphone_{class_name}Type, NULL); + {get_user_data_func_call} if (self == NULL) {{ - {none_trace} - Py_RETURN_NONE; + self = (pylinphone_{class_name}Object *)PyObject_CallObject((PyObject *)&pylinphone_{class_name}Type, NULL); + if (self == NULL) {{ + {none_trace} + Py_RETURN_NONE; + }} + self->native_ptr = ({class_cname} *)native_ptr; + {set_user_data_func_call} + {ref_native_pointer_code} }} - self->native_ptr = ({class_cname} *)native_ptr; - {set_user_data_func_call} - {ref_native_pointer_code} + {incref_code} """.format(class_name=self.class_['class_name'], class_cname=self.class_['class_cname'], - none_trace=self.format_return_none_trace(), set_user_data_func_call=set_user_data_func_call, - ref_native_pointer_code=ref_native_pointer_code) + none_trace=self.format_return_none_trace(), + get_user_data_func_call=get_user_data_func_call, set_user_data_func_call=set_user_data_func_call, + ref_native_pointer_code=ref_native_pointer_code, incref_code=incref_code) def format_return_trace(self): return "\tpylinphone_trace(-1, \"[PYLINPHONE] <<< %s -> %p\", __FUNCTION__, self);\n" @@ -710,13 +717,14 @@ class EventCallbackMethodDefinition(MethodDefinition): PyGILState_STATE pygil_state;""".format(name=self.class_['event_name']) specific = '' for xml_method_arg in self.xml_method_args: - arg_name = 'py' + xml_method_arg.get('name') + arg_name = xml_method_arg.get('name') arg_type = xml_method_arg.get('type') arg_complete_type = xml_method_arg.get('completetype') arg_contained_type = xml_method_arg.get('containedtype') argument_type = ArgumentType(arg_type, arg_complete_type, arg_contained_type, self.linphone_module) if argument_type.fmt_str == 'O': - specific += "\tPyObject * " + arg_name + " = NULL;\n" + specific += "\tPyObject * py" + arg_name + " = NULL;\n" + specific += "\tbool_t incref_" + arg_name + " = FALSE;\n" return "{common}\n{specific}".format(common=common, specific=specific) def format_arguments_parsing(self): @@ -741,7 +749,7 @@ class EventCallbackMethodDefinition(MethodDefinition): return "\tpylinphone_trace(1, \"[PYLINPHONE] >>> %s({fmt})\", __FUNCTION__{args});\n".format(fmt=fmt, args=args) def format_c_function_call(self): - create_python_objects_code = '' + create_python_objects_code = "\t\tPy_INCREF(pylc);\n" decref_python_objects_code = '' fmt = 'O' args = ['pylc'] @@ -761,20 +769,16 @@ class EventCallbackMethodDefinition(MethodDefinition): create_python_objects_code += "\t\tpy{name} = {convert_from_func}({name});\n".format(name=arg_name, convert_from_func=argument_type.convert_from_func) else: type_class = self.find_class_definition(arg_type) - get_user_data_code = '' - new_from_native_pointer_code = "py{name} = pylinphone_{arg_type}_new_from_native_ptr(&pylinphone_{arg_type}Type, {name});".format(name=arg_name, arg_type=strip_leading_linphone(arg_type)) + from_native_pointer_code = "py{name} = pylinphone_{arg_type}_from_native_ptr(&pylinphone_{arg_type}Type, {name}, &incref_{name});".format(name=arg_name, arg_type=strip_leading_linphone(arg_type)) if type_class is not None and type_class['class_has_user_data']: get_user_data_function = type_class['class_c_function_prefix'] + "get_user_data" - get_user_data_code = "py{name} = {get_user_data_function}({name});".format(name=arg_name, get_user_data_function=get_user_data_function) create_python_objects_code += \ -""" {get_user_data_code} - if (py{name} == NULL) {{ - {new_from_native_pointer_code} - }} - Py_INCREF(py{name}); -""".format(name=arg_name, get_user_data_code=get_user_data_code, new_from_native_pointer_code=new_from_native_pointer_code) - decref_python_objects_code += "\t\tPy_DECREF(py{name});\n".format(name=arg_name) +""" {from_native_pointer_code} + if (incref_{name} == TRUE) Py_INCREF(py{name}); +""".format(name=arg_name, from_native_pointer_code=from_native_pointer_code) + decref_python_objects_code += "\t\tif (incref_{name} == TRUE) Py_DECREF(py{name});\n".format(name=arg_name) args=', '.join(args) + decref_python_objects_code += "\t\tPy_DECREF(pylc);" return \ """ if ((func != NULL) && PyCallable_Check(func)) {{ {create_python_objects_code} @@ -973,9 +977,9 @@ class LinphoneModule(object): e.args += (c['class_name'], 'init_body') raise try: - c['new_from_native_pointer_body'] = NewFromNativePointerMethodDefinition(self, c).format() + c['from_native_pointer_body'] = FromNativePointerMethodDefinition(self, c).format() except Exception, e: - e.args += (c['class_name'], 'new_from_native_pointer_body') + e.args += (c['class_name'], 'from_native_pointer_body') raise try: for m in c['class_type_methods']: diff --git a/tools/python/apixml2python/linphone_module.mustache b/tools/python/apixml2python/linphone_module.mustache index bfb6ccfd1..3544d5990 100644 --- a/tools/python/apixml2python/linphone_module.mustache +++ b/tools/python/apixml2python/linphone_module.mustache @@ -62,7 +62,7 @@ typedef struct { {{#classes}} static {{class_cname}} * pylinphone_{{class_name}}_get_native_ptr(PyObject *self); -static PyObject * pylinphone_{{class_name}}_new_from_native_ptr(PyTypeObject *type, const {{class_cname}} *native_ptr); +static PyObject * pylinphone_{{class_name}}_from_native_ptr(PyTypeObject *type, const {{class_cname}} *native_ptr, bool_t *incref); {{#class_type_hand_written_methods}} static PyObject * pylinphone_{{class_name}}_class_method_{{method_name}}(PyObject *cls, PyObject *args); {{/class_type_hand_written_methods}} @@ -75,9 +75,13 @@ static PyObject * pylinphone_{{class_name}}_instance_method_{{method_name}}(PyOb PyObject * PyList_FromMSListOf{{c_contained_type}}(const MSList *msl) { PyObject *pyl = PyList_New(0); while (msl != NULL) { + bool_t incref = FALSE; {{c_contained_type}} *native_ptr = ({{c_contained_type}} *)msl->data; - PyObject *item = pylinphone_{{python_contained_type}}_new_from_native_ptr(&pylinphone_{{python_contained_type}}Type, native_ptr); + PyObject *item = pylinphone_{{python_contained_type}}_from_native_ptr(&pylinphone_{{python_contained_type}}Type, native_ptr, &incref); PyList_Append(pyl, item); + if (incref != TRUE) { + Py_DECREF(item); + } msl = ms_list_next(msl); } return pyl; @@ -107,8 +111,8 @@ static {{class_cname}} * pylinphone_{{class_name}}_get_native_ptr(PyObject *self return ((pylinphone_{{class_name}}Object *)self)->native_ptr; } -static PyObject * pylinphone_{{class_name}}_new_from_native_ptr(PyTypeObject *type, const {{class_cname}} *native_ptr) { -{{{new_from_native_pointer_body}}} +static PyObject * pylinphone_{{class_name}}_from_native_ptr(PyTypeObject *type, const {{class_cname}} *native_ptr, bool_t *incref) { +{{{from_native_pointer_body}}} } static PyObject * pylinphone_{{class_name}}_new(PyTypeObject *type, PyObject *args, PyObject *kw) {