diff --git a/dtool/src/cppparser/cppBison.yxx b/dtool/src/cppparser/cppBison.yxx index 0d7cced156..24b078417e 100644 --- a/dtool/src/cppparser/cppBison.yxx +++ b/dtool/src/cppparser/cppBison.yxx @@ -236,6 +236,7 @@ pop_struct() { %token TOKENPASTE %token KW_BEGIN_PUBLISH +%token KW_BLOCKING %token KW_BOOL %token KW_CATCH %token KW_CHAR @@ -531,6 +532,10 @@ storage_class: | storage_class KW_REGISTER { $$ = $1 | (int)CPPInstance::SC_register; +} + | storage_class KW_BLOCKING +{ + $$ = $1 | (int)CPPInstance::SC_blocking; } ; diff --git a/dtool/src/cppparser/cppInstance.h b/dtool/src/cppparser/cppInstance.h index b5c9beac78..71ac325627 100644 --- a/dtool/src/cppparser/cppInstance.h +++ b/dtool/src/cppparser/cppInstance.h @@ -54,6 +54,12 @@ public: // This bit is only set by CPPStructType::check_virtual(). SC_inherited_virtual = 0x400, + + // This is a special "storage class" for methods tagged with the + // BLOCKING macro (i.e. the special __blocking keyword). These + // are methods that might block and therefore need to release + // Python threads for their duration. + SC_blocking = 0x800, }; CPPInstance(CPPType *type, const string &name, int storage_class = 0); diff --git a/dtool/src/cppparser/cppPreprocessor.cxx b/dtool/src/cppparser/cppPreprocessor.cxx index 2184a96ac8..83b6381fde 100644 --- a/dtool/src/cppparser/cppPreprocessor.cxx +++ b/dtool/src/cppparser/cppPreprocessor.cxx @@ -1929,6 +1929,7 @@ get_number(int c, int c2) { int CPPPreprocessor:: check_keyword(const string &name) { if (name == "__begin_publish") return KW_BEGIN_PUBLISH; + if (name == "__blocking") return KW_BLOCKING; if (name == "bool") return KW_BOOL; if (name == "catch") return KW_CATCH; if (name == "char") return KW_CHAR; diff --git a/dtool/src/dtoolbase/dtoolbase.h b/dtool/src/dtoolbase/dtoolbase.h index a66084f7ad..ece3277bbd 100644 --- a/dtool/src/dtoolbase/dtoolbase.h +++ b/dtool/src/dtoolbase/dtoolbase.h @@ -277,15 +277,19 @@ /* We define the macros BEGIN_PUBLISH and END_PUBLISH to bracket functions and global variable definitions that are to be published - via interrogate to scripting languages. + via interrogate to scripting languages. Also, the macro BLOCKING is + used to flag any function or method that might perform I/O blocking + and thus needs to release Python threads for its duration. */ #ifdef CPPPARSER #define BEGIN_PUBLISH __begin_publish #define END_PUBLISH __end_publish +#define BLOCKING __blocking #undef USE_STL_ALLOCATOR // Don't try to parse these template classes in interrogate. #else #define BEGIN_PUBLISH #define END_PUBLISH +#define BLOCKING #endif #ifdef __cplusplus diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx index 9265c85b52..c6a1b7552c 100644 --- a/dtool/src/interrogate/functionRemap.cxx +++ b/dtool/src/interrogate/functionRemap.cxx @@ -47,6 +47,7 @@ FunctionRemap(const InterrogateType &itype, const InterrogateFunction &ifunc, _void_return = true; _ForcedVoidReturn = false; _has_this = false; + _blocking = false; _const_method = false; _first_true_parameter = 0; _num_default_parameters = num_default_parameters; @@ -438,6 +439,12 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak _type = T_setter; } + if (_cpptype != (CPPType *)NULL && + ((_cppfunc->_storage_class & CPPInstance::SC_blocking) != 0)) { + // If it's marked as a "blocking" method or function, record that. + _blocking = true; + } + if (_cpptype != (CPPType *)NULL && ((_cppfunc->_storage_class & CPPInstance::SC_static) == 0) && _type != T_constructor) { diff --git a/dtool/src/interrogate/functionRemap.h b/dtool/src/interrogate/functionRemap.h index c57f5318ad..fd32436354 100644 --- a/dtool/src/interrogate/functionRemap.h +++ b/dtool/src/interrogate/functionRemap.h @@ -89,6 +89,7 @@ public: bool _void_return; bool _ForcedVoidReturn; bool _has_this; + bool _blocking; bool _const_method; int _first_true_parameter; int _num_default_parameters; diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 647ae63832..f7361b00b7 100755 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -2275,6 +2275,12 @@ void InterfaceMakerPythonNative::write_function_instance(ostream &out, Interface remap->_return_type->new_type_is_atomic_string()) { // Treat strings as a special case. We don't want to format the // return expression. + if (remap->_blocking) { + indent(out, extra_indent_level) + << "PyThreadState *_save;\n"; + indent(out, extra_indent_level) + << "Py_UNBLOCK_THREADS\n"; + } string tt; string return_expr = remap->call_function(out, extra_indent_level, false, container, pexprs); CPPType *type = remap->_return_type->get_orig_type(); @@ -2282,6 +2288,11 @@ void InterfaceMakerPythonNative::write_function_instance(ostream &out, Interface type->output_instance(out, "return_value", &parser); // type->output_instance(tt, "return_value", &parser); out << " = " << return_expr << ";\n"; + + if (remap->_blocking) { + indent(out, extra_indent_level) + << "Py_BLOCK_THREADS\n"; + } if (track_interpreter) { indent(out,extra_indent_level) << "in_interpreter = 1;\n"; @@ -2295,8 +2306,20 @@ void InterfaceMakerPythonNative::write_function_instance(ostream &out, Interface pack_return_value(out, extra_indent_level, remap, return_expr,ForwardDeclrs,is_inplace); } else { + if (remap->_blocking) { + indent(out, extra_indent_level) + << "PyThreadState *_save;\n"; + indent(out, extra_indent_level) + << "Py_UNBLOCK_THREADS\n"; + } + string return_expr = remap->call_function(out, extra_indent_level, true, container, pexprs); if (return_expr.empty()) { + if (remap->_blocking) { + indent(out, extra_indent_level) + << "Py_BLOCK_THREADS\n"; + } + if (track_interpreter) { indent(out,extra_indent_level) << "in_interpreter = 1;\n"; } @@ -2313,6 +2336,11 @@ void InterfaceMakerPythonNative::write_function_instance(ostream &out, Interface type->output_instance(out, "return_value", &parser); out << " = " << return_expr << ";\n"; } + if (remap->_blocking) { + indent(out, extra_indent_level) + << "Py_BLOCK_THREADS\n"; + } + if (track_interpreter) { indent(out,extra_indent_level) << "in_interpreter = 1;\n"; }