From 34ce8d0bcc7d975754a6fba098c8bd7c8e7bf580 Mon Sep 17 00:00:00 2001 From: Krishna Gadepalli <34969407+kkg4theweb@users.noreply.github.com> Date: Thu, 30 Sep 2021 12:13:14 -0700 Subject: [PATCH] Issue# 1762 - Add an option to build SWIG python bindings with threads enabled (#1763) * Enable configure builds with SWIG threading and/or debugging enabled * Surround python codeblock that need to be thread-safe with SWIG_PYTHON_THREAD_{BEGIN,END}_BLOCK * define and use SWIG_PYTHON_THREAD_SCOPED_BLOCK to make py calls GIL-safe * enable swig threads for the PYv3.9/MPI CI test * Add docs describing the new feature. * Add more comments describing the new SWIG_PYTHON_THREAD_SCOPED_BLOCK macro. * switch the MEEP_SWIG_PYTHON_DEBUG to use master_printf --- .github/workflows/build-ci.yml | 4 +- configure.ac | 27 ++++++++ doc/docs/Python_Developer_Information.md | 15 +++- python/Makefile.am | 6 +- python/meep-python.hpp | 12 +++- python/meep.i | 75 ++++++++++++++++---- python/typemap_utils.cpp | 88 ++++++++++++++++++++++++ src/meep/meep-config.h.in | 13 ++++ 8 files changed, 220 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index c584c3d72..f1b70960f 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -151,12 +151,12 @@ jobs: if: ${{ matrix.enable-mpi == false && matrix.python-version == 3.6 }} run: ./configure --enable-maintainer-mode --with-coverage --prefix=${HOME}/local --with-libctl=${HOME}/local/share/libctl ${MPICONF} - - name: Run configure with single-precision floating point + - name: Run configure with single-precision floating point and swig threads if: ${{ matrix.enable-mpi == true && matrix.python-version == 3.9 }} run: | mkdir -p build && pushd build && - ../configure --enable-maintainer-mode --prefix=${HOME}/local --with-libctl=${HOME}/local/share/libctl ${MPICONF} --enable-single && + ../configure --enable-maintainer-mode --prefix=${HOME}/local --with-libctl=${HOME}/local/share/libctl ${MPICONF} --enable-single --enable-swig-python-threads && popd - name: Run make diff --git a/configure.ac b/configure.ac index 0e15686eb..075f991e6 100644 --- a/configure.ac +++ b/configure.ac @@ -31,6 +31,33 @@ else fi AC_SUBST(MEEP_SINGLE) +############################################################################## +# build with SWIG threads for Python + +AC_ARG_ENABLE(swig-python-threads, + [AS_HELP_STRING([--enable-swig-python-threads],[enable SWIG threads for Python bindings])], + enable_swig_python_threads=$enableval, enable_swig_python_threads=no) +if test "x$enable_swig_python_threads" = "xyes"; then + MEEP_SWIG_PYTHON_THREADS=1 +else + MEEP_SWIG_PYTHON_THREADS=0 +fi +AC_SUBST(MEEP_SWIG_PYTHON_THREADS) +AM_CONDITIONAL(MEEP_SWIG_PYTHON_THREADS, test "x$enable_swig_python_threads" = "xyes") + +############################################################################## +# build with SWIG debugging enabled for Python + +AC_ARG_ENABLE(swig-python-debug, + [AS_HELP_STRING([--enable-swig-python-debug],[enable SWIG debug for Python bindings])], + enable_swig_python_debug=$enableval, enable_swig_python_debug=no) +if test "x$enable_swig_python_debug" = "xyes"; then + MEEP_SWIG_PYTHON_DEBUG=1 +else + MEEP_SWIG_PYTHON_DEBUG=0 +fi +AC_SUBST(MEEP_SWIG_PYTHON_DEBUG) + ############################################################################## # Check for mpiCC immediately after getting C++ compiler... diff --git a/doc/docs/Python_Developer_Information.md b/doc/docs/Python_Developer_Information.md index d8f97ca9e..a9853b2ad 100644 --- a/doc/docs/Python_Developer_Information.md +++ b/doc/docs/Python_Developer_Information.md @@ -91,7 +91,20 @@ Classes and functions related to the high-level Python interface to `MPB`. Addit Definition of `MPBData`, a Python class useful for `MPB` data analysis (documented [here](https://mpb.readthedocs.io/en/latest/Python_Data_Analysis_Tutorial)). This is is a Python port of the functionality available in the [`mpb-data` command line program](https://github.com/NanoComp/mpb/blob/master/utils/mpb-data.c) originally written in C. -## Development and Testing +## Development + +By default, the SWIG Python bindings are built with `threads` disabled (GIL is +held for all SWIG wrapped python calls by default). You can optionally build the +Python bindings with `threads` enabled (releasing the GIL for all SWIG wrapped +Python calls) by passing the `--enable-swig-python-threads` +option to the configure script. + +Since the bindings could be built with `threads` enabled, one needs to be +careful to protect (acquire the GIL) code that calls back into Python or custom +python wrapper code that uses PyAPI. Look for `SWIG_PYTHON_THREAD_SCOPED_BLOCK` +in the SWIG interface files or the custom wrapper code for how this is done. + +## Testing The tests for the Python interface are located in `python/tests`. To run the whole test suite, run `make check` in the `python` build tree. During development it is more convenient to run individual tests. This can be accomplished by running `python /test.py MyTestCase.test_method`. See the [Python unittest framework documentation](https://docs.python.org/3/library/unittest.html) for more info. diff --git a/python/Makefile.am b/python/Makefile.am index d003aad9c..c203d20be 100644 --- a/python/Makefile.am +++ b/python/Makefile.am @@ -164,8 +164,12 @@ HPPFILES= \ $(top_srcdir)/src/adjust_verbosity.hpp \ meep-python.hpp +if MEEP_SWIG_PYTHON_THREADS +SWIG_MEEP_FLAGS = -threads +endif # MEEP_SWIG_PYTHON_THREADS + meep-python.cxx: $(MEEP_SWIG_SRC) $(HPPFILES) - $(SWIG) -Wextra $(AM_CPPFLAGS) -outdir $(builddir) -c++ -nofastunpack -python -o $@ $(srcdir)/meep.i + $(SWIG) -Wextra $(SWIG_MEEP_FLAGS) $(AM_CPPFLAGS) -outdir $(builddir) -c++ -nofastunpack -python -o $@ $(srcdir)/meep.i if WITH_MPB MPB_SWIG_SRC = mpb.i diff --git a/python/meep-python.hpp b/python/meep-python.hpp index 04066a9aa..e254f8f26 100644 --- a/python/meep-python.hpp +++ b/python/meep-python.hpp @@ -1,14 +1,22 @@ namespace meep { +#ifndef SWIG_PYTHON_THREAD_SCOPED_BLOCK +#define SWIG_PYTHON_THREAD_SCOPED_BLOCK SWIG_PYTHON_THREAD_BEGIN_BLOCK +#endif + // like custom_src_time, but using Python function object, with proper reference counting class custom_py_src_time : public src_time { public: custom_py_src_time(PyObject *fun, double st = -infinity, double et = infinity, std::complex f = 0) : func(fun), freq(f), start_time(float(st)), end_time(float(et)) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; Py_INCREF(func); } - virtual ~custom_py_src_time() { Py_DECREF(func); } + virtual ~custom_py_src_time() { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; + Py_DECREF(func); + } virtual std::complex current(double time, double dt) const { if (is_integrated) @@ -17,6 +25,7 @@ class custom_py_src_time : public src_time { return dipole(time); } virtual std::complex dipole(double time) const { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; float rtime = float(time); if (rtime >= start_time && rtime <= end_time) { PyObject *py_t = PyFloat_FromDouble(time); @@ -33,6 +42,7 @@ class custom_py_src_time : public src_time { } virtual double last_time() const { return end_time; }; virtual src_time *clone() const { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; Py_INCREF(func); // default copy constructor doesn't incref return new custom_py_src_time(*this); } diff --git a/python/meep.i b/python/meep.i index 6f2a75a5f..ce1f2a713 100644 --- a/python/meep.i +++ b/python/meep.i @@ -23,6 +23,23 @@ #define SWIG_FILE_WITH_INIT #define SWIG_PYTHON_2_UNICODE +/* + * In C++ we can use a scoped variable to acquire the GIL and then auto release + * on leaving scope, making our code a bit cleaner. + * + * SWIG_PYTHON_THREAD_SCOPED_BLOCK is a macro that SWIG automatically generates + * wrapping a class using an RAII pattern to automatically acquire/release + * the GIL. See the generated meep-python.cxx for details. + * + * We could instead just explicitly call SWIG_PYTHON_THREAD_BEGIN_BLOCK and + * SWIG_PYTHON_THREAD_END_BLOCK everywhere - but this is error prone since we + * have to ensure that SWIG_PYTHON_THREAD_END_BLOCK is called before every + * return statement in a method. + * + * NOTE: This wont work with plain-old C. + */ +#define SWIG_PYTHON_THREAD_SCOPED_BLOCK SWIG_PYTHON_THREAD_BEGIN_BLOCK + #include #include @@ -112,6 +129,7 @@ static PyObject *py_meep_src_time_object() { } static double py_callback_wrap(const meep::vec &v) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *pyv = vec2py(v); PyObject *pyret = PyObject_CallFunctionObjArgs(py_callback, pyv, NULL); double ret = PyFloat_AsDouble(pyret); @@ -121,6 +139,7 @@ static double py_callback_wrap(const meep::vec &v) { } static std::complex py_amp_func_wrap(const meep::vec &v) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *pyv = vec2py(v); PyObject *pyret = PyObject_CallFunctionObjArgs(py_amp_func, pyv, NULL); double real = PyComplex_RealAsDouble(pyret); @@ -134,6 +153,7 @@ static std::complex py_amp_func_wrap(const meep::vec &v) { static std::complex py_field_func_wrap(const std::complex *fields, const meep::vec &loc, void *data_) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *pyv = vec2py(loc); py_field_func_data *data = (py_field_func_data *)data_; @@ -163,37 +183,35 @@ static std::complex py_field_func_wrap(const std::complex *field } static meep::vec py_kpoint_func_wrap(double freq, int mode, void *user_data) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *py_freq = PyFloat_FromDouble(freq); PyObject *py_mode = PyInteger_FromLong(mode); PyObject *py_result = PyObject_CallFunctionObjArgs((PyObject*)user_data, py_freq, py_mode, NULL); - if (!py_result) { - PyErr_PrintEx(0); - Py_DECREF(py_freq); - Py_DECREF(py_mode); - return meep::vec(0, 0, 0); - } + meep::vec result; - vector3 v3; - if (!pyv3_to_v3(py_result, &v3)) { + if (!py_result) { PyErr_PrintEx(0); - Py_DECREF(py_freq); - Py_DECREF(py_mode); + result = meep::vec(0, 0, 0); + } else { + vector3 v3; + if (!pyv3_to_v3(py_result, &v3)) { + PyErr_PrintEx(0); + result = meep::vec(0, 0, 0); + } else { + result = meep::vec(v3.x, v3.y, v3.z); + } Py_XDECREF(py_result); - return meep::vec(0, 0, 0); } - meep::vec result(v3.x, v3.y, v3.z); - Py_DECREF(py_freq); Py_DECREF(py_mode); - Py_DECREF(py_result); - return result; } static void _do_master_printf(const char* stream_name, const char* text) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *py_stream = PySys_GetObject((char*)stream_name); // arg is non-const on Python2 Py_XDECREF(PyObject_CallMethod(py_stream, "write", "(s)", text)); @@ -248,6 +266,7 @@ static int pyabsorber_to_absorber(PyObject *py_absorber, meep_geom::absorber *a) // Wrapper for Python PML profile function double py_pml_profile(double u, void *f) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *func = (PyObject *)f; PyObject *d = PyFloat_FromDouble(u); @@ -573,6 +592,7 @@ meep::eigenmode_data *_get_eigenmode(meep::fields *f, double frequency, meep::di } PyObject *_get_eigenmode_Gk(meep::eigenmode_data *emdata) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value: New reference PyObject *v3_class = py_vector3_object(); PyObject *args = Py_BuildValue("(ddd)", emdata->Gk[0], emdata->Gk[1], emdata->Gk[2]); @@ -594,6 +614,24 @@ void _get_eigenmode(meep::fields *f, double frequency, meep::direction d, const #endif %} +/* + * These methods extensively use the Python C api (especially allocation) and + * hence need to hold the GIL (acquire/release) for key parts of their + * implementaion/code. Instead, disable threading for these methods by default. + * + * TODO: If any of these methods are expensive, we can explicitly allow threads + * for the expensive blocks of code in these methods. + */ +%feature("nothreadallow") _dft_ldos_J; +%feature("nothreadallow") _dft_ldos_F; +%feature("nothreadallow") _dft_ldos_ldos; +%feature("nothreadallow") _get_farfields_array; +%feature("nothreadallow") _get_farfield; +%feature("nothreadallow") py_do_harminv; +%feature("nothreadallow") _get_array_slice_dimensions; +%feature("nothreadallow") _get_gradient; +%feature("nothreadallow") _get_dft_array; + %numpy_typemaps(std::complex, NPY_CDOUBLE, int); %numpy_typemaps(std::complex, NPY_CDOUBLE, size_t); @@ -867,6 +905,7 @@ void _get_gradient(PyObject *grad, PyObject *fields_a, PyObject *fields_f, PyObj Py_DECREF(swigobj); } %} + //-------------------------------------------------- // end typemaps needed for material grid //-------------------------------------------------- @@ -1410,6 +1449,11 @@ void _get_gradient(PyObject *grad, PyObject *fields_a, PyObject *fields_f, PyObj } %exception { +#ifdef MEEP_SWIG_PYTHON_DEBUG + // NOTE: You can do fancier things like timing the calls and using that + // to track the most expensive calls etc. + master_printf("**SWIG**: $symname\n"); +#endif try { $action } catch (std::runtime_error &e) { @@ -1522,6 +1566,7 @@ void _get_gradient(PyObject *grad, PyObject *fields_a, PyObject *fields_f, PyObj %template(get_dft_fields_array) _get_dft_array; %template(get_dft_force_array) _get_dft_array; %template(get_dft_near2far_array) _get_dft_array; + %template(FragmentStatsVector) std::vector; %template(DftDataVector) std::vector; %template(VolumeVector) std::vector; diff --git a/python/typemap_utils.cpp b/python/typemap_utils.cpp index 1aea846da..0602a72ff 100644 --- a/python/typemap_utils.cpp +++ b/python/typemap_utils.cpp @@ -31,6 +31,10 @@ #define PyInteger_FromLong(n) PyInt_FromLong(n) #endif +#ifndef SWIG_PYTHON_THREAD_SCOPED_BLOCK +#define SWIG_PYTHON_THREAD_SCOPED_BLOCK SWIG_PYTHON_THREAD_BEGIN_BLOCK +#endif + PyObject *py_callback = NULL; PyObject *py_callback_v3 = NULL; PyObject *py_amp_func = NULL; @@ -45,6 +49,7 @@ static int pymaterial_to_material(PyObject *po, material_type *mt); #if PY_MAJOR_VERSION == 2 static char *py2_string_as_utf8(PyObject *po) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; if (PyString_Check(po)) { return PyString_AsString(po); } else if (PyUnicode_Check(po)) { PyObject *s = PyUnicode_AsUTF8String(po); @@ -59,6 +64,7 @@ static char *py2_string_as_utf8(PyObject *po) { #endif static PyObject *get_meep_mod() { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value: Borrowed reference static PyObject *meep_mod = NULL; if (meep_mod == NULL) { meep_mod = PyImport_ImportModule("meep"); } @@ -66,6 +72,7 @@ static PyObject *get_meep_mod() { } static PyObject *get_geom_mod() { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value: Borrowed reference static PyObject *geom_mod = NULL; if (geom_mod == NULL) { geom_mod = PyImport_ImportModule("meep.geom"); } @@ -73,6 +80,7 @@ static PyObject *get_geom_mod() { } static PyObject *py_material_object() { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value; Borrowed reference static PyObject *material_object = NULL; if (material_object == NULL) { @@ -83,6 +91,7 @@ static PyObject *py_material_object() { } static PyObject *py_material_grid_object() { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value: Borrowed reference static PyObject *material_object = NULL; if (material_object == NULL) { @@ -90,9 +99,11 @@ static PyObject *py_material_grid_object() { material_object = PyObject_GetAttrString(geom_mod, "MaterialGrid"); } return material_object; + ; } static PyObject *py_vector3_object() { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value: Borrowed reference static PyObject *vector3_object = NULL; if (vector3_object == NULL) { @@ -100,9 +111,11 @@ static PyObject *py_vector3_object() { vector3_object = PyObject_GetAttrString(geom_mod, "Vector3"); } return vector3_object; + ; } static PyObject *py_volume_object() { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value: Borrowed reference static PyObject *volume_object = NULL; if (volume_object == NULL) { @@ -110,9 +123,11 @@ static PyObject *py_volume_object() { volume_object = PyObject_GetAttrString(meep_mod, "Volume"); } return volume_object; + ; } static PyObject *vec2py(const meep::vec &v, bool newobj = false) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value: New reference double x = 0, y = 0, z = 0; @@ -166,9 +181,11 @@ static PyObject *vec2py(const meep::vec &v, bool newobj = false) { Py_INCREF(py_callback_v3); return py_callback_v3; } + ; } static void py_user_material_func_wrap(vector3 x, void *user_data, medium_struct *medium) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *py_vec = vec2py(vector3_to_vec(x)); PyObject *pyret = PyObject_CallFunctionObjArgs((PyObject *)user_data, py_vec, NULL); @@ -179,9 +196,11 @@ static void py_user_material_func_wrap(vector3 x, void *user_data, medium_struct Py_DECREF(py_vec); Py_DECREF(pyret); + ; } static void py_epsilon_func_wrap(vector3 x, void *user_data, medium_struct *medium) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *py_vec = vec2py(vector3_to_vec(x)); PyObject *pyret = PyObject_CallFunctionObjArgs((PyObject *)user_data, py_vec, NULL); @@ -196,9 +215,11 @@ static void py_epsilon_func_wrap(vector3 x, void *user_data, medium_struct *medi Py_DECREF(py_vec); Py_DECREF(pyret); + ; } static std::string py_class_name_as_string(PyObject *po) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *py_type = PyObject_Type(po); PyObject *name = PyObject_GetAttrString(py_type, "__name__"); @@ -210,9 +231,11 @@ static std::string py_class_name_as_string(PyObject *po) { Py_XDECREF(name); return class_name; + ; } static int pyv3_to_v3(PyObject *po, vector3 *v) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *py_x = PyObject_GetAttrString(po, "x"); PyObject *py_y = PyObject_GetAttrString(po, "y"); @@ -232,9 +255,11 @@ static int pyv3_to_v3(PyObject *po, vector3 *v) { v->z = z; return 1; + ; } static int pyv3_to_cv3(PyObject *po, cvector3 *v) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *py_x = PyObject_GetAttrString(po, "x"); PyObject *py_y = PyObject_GetAttrString(po, "y"); PyObject *py_z = PyObject_GetAttrString(po, "z"); @@ -259,9 +284,11 @@ static int pyv3_to_cv3(PyObject *po, cvector3 *v) { v->z.im = z.imag(); return 1; + ; } static PyObject *v3_to_pyv3(vector3 *v) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value: New reference PyObject *v3_class = py_vector3_object(); PyObject *args = Py_BuildValue("(ddd)", v->x, v->y, v->z); @@ -270,9 +297,11 @@ static PyObject *v3_to_pyv3(vector3 *v) { Py_DECREF(args); return py_v; + ; } static int get_attr_v3(PyObject *py_obj, vector3 *v, const char *name) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; int rval = 1; PyObject *py_attr = PyObject_GetAttrString(py_obj, name); @@ -282,9 +311,11 @@ static int get_attr_v3(PyObject *py_obj, vector3 *v, const char *name) { Py_XDECREF(py_attr); return rval; + ; } static int get_attr_v3_cmplx(PyObject *py_obj, cvector3 *v, const char *name) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; int rval = 1; PyObject *py_attr = PyObject_GetAttrString(py_obj, name); @@ -294,9 +325,11 @@ static int get_attr_v3_cmplx(PyObject *py_obj, cvector3 *v, const char *name) { Py_XDECREF(py_attr); return rval; + ; } static int get_attr_dbl(PyObject *py_obj, double *result, const char *name) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *py_attr = PyObject_GetAttrString(py_obj, name); if (!py_attr) { abort_with_stack_trace(); } @@ -304,9 +337,11 @@ static int get_attr_dbl(PyObject *py_obj, double *result, const char *name) { *result = PyFloat_AsDouble(py_attr); Py_XDECREF(py_attr); return 1; + ; } static int get_attr_int(PyObject *py_obj, int *result, const char *name) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *py_attr = PyObject_GetAttrString(py_obj, name); if (!py_attr) { abort_with_stack_trace(); } @@ -314,9 +349,11 @@ static int get_attr_int(PyObject *py_obj, int *result, const char *name) { *result = PyInteger_AsLong(py_attr); Py_XDECREF(py_attr); return 1; + ; } static int get_attr_material(PyObject *po, material_type *m) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; int rval = 1; PyObject *py_material = PyObject_GetAttrString(po, "material"); @@ -327,9 +364,11 @@ static int get_attr_material(PyObject *po, material_type *m) { Py_XDECREF(py_material); return rval; + ; } static int pytransition_to_transition(PyObject *py_trans, transition *trans) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; int from, to; double trans_rate, freq, gamma, pump_rate; @@ -355,9 +394,11 @@ static int pytransition_to_transition(PyObject *py_trans, transition *trans) { trans->sigma_diag.z = sigma_diag.z; return 1; + ; } static int py_susceptibility_to_susceptibility(PyObject *po, susceptibility_struct *s) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; if (!get_attr_v3(po, &s->sigma_diag, "sigma_diag") || !get_attr_v3(po, &s->sigma_offdiag, "sigma_offdiag")) { @@ -429,9 +470,11 @@ static int py_susceptibility_to_susceptibility(PyObject *po, susceptibility_stru s->is_file = false; return 1; + ; } static int py_list_to_susceptibility_list(PyObject *po, susceptibility_list *sl) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; if (!PyList_Check(po)) { abort_with_stack_trace(); } int length = PyList_Size(po); @@ -442,9 +485,11 @@ static int py_list_to_susceptibility_list(PyObject *po, susceptibility_list *sl) } return 1; + ; } static int pymaterial_grid_to_material_grid(PyObject *po, material_data *md) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // po must be a python MaterialGrid object int rval = 1; @@ -515,9 +560,11 @@ static int pymaterial_grid_to_material_grid(PyObject *po, material_data *md) { Py_XDECREF(py_sus); return rval; + ; } static int pymaterial_to_material(PyObject *po, material_type *mt) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; material_data *md; if (PyObject_IsInstance(po, py_material_object())) { @@ -581,9 +628,11 @@ static int pymaterial_to_material(PyObject *po, material_type *mt) { *mt = md; return 1; + ; } template static void set_v3_on_pyobj(PyObject *py_obj, const T *v3, const char *attr) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *v3_class = py_vector3_object(); PyObject *v3_args = Py_BuildValue("(d,d,d)", v3->x, v3->y, v3->z); PyObject *pyv3 = PyObject_Call(v3_class, v3_args, NULL); @@ -591,9 +640,11 @@ template static void set_v3_on_pyobj(PyObject *py_obj, const T *v3, co Py_DECREF(v3_args); Py_DECREF(pyv3); + ; } static PyObject *susceptibility_to_py_obj(const susceptibility_struct *s) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value: New reference PyObject *geom_mod = get_geom_mod(); @@ -669,9 +720,11 @@ static PyObject *susceptibility_to_py_obj(const susceptibility_struct *s) { Py_DECREF(py_gamma); return res; + ; } static PyObject *susceptibility_list_to_py_list(const susceptibility_list *sl) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value: New reference PyObject *res = PyList_New(sl->size()); @@ -680,9 +733,11 @@ static PyObject *susceptibility_list_to_py_list(const susceptibility_list *sl) { } return res; + ; } static PyObject *material_to_py_material(material_type mat) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value: New reference switch (mat->which_subclass) { case meep_geom::material_data::MEDIUM: { @@ -724,9 +779,11 @@ static PyObject *material_to_py_material(material_type mat) { // Only Medium is supported at this time. meep::abort("Can only convert C++ medium_struct subtype %d to Python", mat->which_subclass); } + ; } static int pymedium_to_medium(PyObject *po, medium_struct *m) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; if (!get_attr_v3(po, &m->epsilon_diag, "epsilon_diag") || !get_attr_v3(po, &m->mu_diag, "mu_diag")) { @@ -768,9 +825,11 @@ static int pymedium_to_medium(PyObject *po, medium_struct *m) { } return 1; + ; } static int pysphere_to_sphere(PyObject *py_sphere, geometric_object *go) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; material_type material; vector3 center; @@ -786,9 +845,11 @@ static int pysphere_to_sphere(PyObject *py_sphere, geometric_object *go) { *go = make_sphere(material, center, radius); return 1; + ; } static int pycylinder_to_cylinder(PyObject *py_cyl, geometric_object *cyl) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; material_type material; vector3 center, axis; double radius, height; @@ -804,9 +865,11 @@ static int pycylinder_to_cylinder(PyObject *py_cyl, geometric_object *cyl) { *cyl = make_cylinder(material, center, radius, height, axis); return 1; + ; } static int pywedge_to_wedge(PyObject *py_wedge, geometric_object *wedge) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; geometric_object cyl; if (!pycylinder_to_cylinder(py_wedge, &cyl)) { return 0; } @@ -830,9 +893,11 @@ static int pywedge_to_wedge(PyObject *py_wedge, geometric_object *wedge) { geometric_object_destroy(cyl); return 1; + ; } static int pycone_to_cone(PyObject *py_cone, geometric_object *cone) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; geometric_object cyl; if (!pycylinder_to_cylinder(py_cone, &cyl)) { return 0; } @@ -852,9 +917,11 @@ static int pycone_to_cone(PyObject *py_cone, geometric_object *cone) { geometric_object_destroy(cyl); return 1; + ; } static int pyblock_to_block(PyObject *py_blk, geometric_object *blk) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; material_type material; vector3 center, e1, e2, e3, size; @@ -868,9 +935,11 @@ static int pyblock_to_block(PyObject *py_blk, geometric_object *blk) { *blk = make_block(material, center, e1, e2, e3, size); return 1; + ; } static int pyellipsoid_to_ellipsoid(PyObject *py_ell, geometric_object *e) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; geometric_object blk; if (!pyblock_to_block(py_ell, &blk)) { return 0; } @@ -886,9 +955,11 @@ static int pyellipsoid_to_ellipsoid(PyObject *py_ell, geometric_object *e) { geometric_object_destroy(blk); return 1; + ; } static int pyprism_to_prism(PyObject *py_prism, geometric_object *p) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; material_type material; double height, sidewall_angle; vector3 axis, center; @@ -931,9 +1002,11 @@ static int pyprism_to_prism(PyObject *py_prism, geometric_object *p) { Py_DECREF(py_vert_list); return 1; + ; } static int py_gobj_to_gobj(PyObject *po, geometric_object *o) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; int success = 0; std::string go_type = py_class_name_as_string(po); @@ -962,9 +1035,11 @@ static int py_gobj_to_gobj(PyObject *po, geometric_object *o) { } return success; + ; } static int py_list_to_gobj_list(PyObject *po, geometric_object_list *l) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; if (!PyList_Check(po)) { meep::abort("Expected a list"); } int length = PyList_Size(po); @@ -978,9 +1053,11 @@ static int py_list_to_gobj_list(PyObject *po, geometric_object_list *l) { } return 1; + ; } static PyObject *gobj_to_py_obj(geometric_object *gobj) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value: New reference switch (gobj->which_subclass) { case geometric_object::PRISM: { @@ -1023,9 +1100,11 @@ static PyObject *gobj_to_py_obj(geometric_object *gobj) { // Other geometry can be added as needed. meep::abort("Conversion of non-prism geometric_object to Python is not supported"); } + ; } static PyObject *gobj_list_to_py_list(geometric_object_list *objs) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value: New reference PyObject *py_res = PyList_New(objs->num_items); @@ -1037,18 +1116,22 @@ static PyObject *gobj_list_to_py_list(geometric_object_list *objs) { delete[] objs->items; return py_res; + ; } void gobj_list_freearg(geometric_object_list* objs) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; for(int i = 0; i < objs->num_items; ++i) { material_free((material_data *)objs->items[i].material); delete (material_data *)objs->items[i].material; geometric_object_destroy(objs->items[i]); } delete[] objs->items; + ; } static std::unique_ptr py_bp_to_bp(PyObject *pybp) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; std::unique_ptr bp; if (pybp == Py_None) return bp; @@ -1076,19 +1159,23 @@ static std::unique_ptr py_bp_to_bp(PyObject *pybp) { Py_XDECREF(left); Py_XDECREF(right); return bp; + ; } static PyObject *py_binary_partition_object() { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; // Return value: Borrowed reference static PyObject *bp_type = NULL; if (bp_type == NULL) { bp_type = PyObject_GetAttrString(get_meep_mod(), "BinaryPartition"); } return bp_type; + ; } // Converts a meep::binary_partition object into a Python class instance static PyObject *bp_to_py_bp(const meep::binary_partition *bp) { + SWIG_PYTHON_THREAD_SCOPED_BLOCK; PyObject *bp_class = py_binary_partition_object(); PyObject *args = PyTuple_New(0); // no numbered arguments to pass if (bp->is_leaf()) { @@ -1113,4 +1200,5 @@ static PyObject *bp_to_py_bp(const meep::binary_partition *bp) { Py_DECREF(kwargs); return py_bp; } + ; } diff --git a/src/meep/meep-config.h.in b/src/meep/meep-config.h.in index 109ab154a..f5bf1d282 100644 --- a/src/meep/meep-config.h.in +++ b/src/meep/meep-config.h.in @@ -1,5 +1,18 @@ /* compile-time configuration of meep that affects user linkage */ +// Enable single precision builds. #ifndef MEEP_SINGLE #define MEEP_SINGLE @MEEP_SINGLE@ #endif + +// Enable SWIG threads for Python. When enabled, the GIL is released for +// all SWIG wrapped calls by defaults. +#ifndef MEEP_SWIG_PYTHON_THREADS +#define MEEP_SWIG_PYTHON_THREADS @MEEP_SWIG_PYTHON_THREADS@ +#endif + +// Enable debugging for SWIG wrapped calls for Python. When enabled, all +// SWIG wrapped python calls are logged. +#ifndef MEEP_SWIG_PYTHON_DEBUG +#define MEEP_SWIG_PYTHON_DEBUG @MEEP_SWIG_PYTHON_DEBUG@ +#endif