diff --git a/gtwrap/matlab_wrapper/templates.py b/gtwrap/matlab_wrapper/templates.py index bff1650..31de924 100644 --- a/gtwrap/matlab_wrapper/templates.py +++ b/gtwrap/matlab_wrapper/templates.py @@ -26,6 +26,16 @@ class WrapperTemplate: '''), prefix=' ') + tbb_headers = textwrap.dedent(""" + #ifdef GTSAM_USE_TBB + #include + #if TBB_INTERFACE_VERSION >= 12060 + #include + #define GTSAM_WRAP_TBB_FINALIZE 1 + #endif + #endif + """) + delete_all_objects = textwrap.dedent(''' void _deleteAllObjects() {{ @@ -39,6 +49,16 @@ class WrapperTemplate: "calling destructors, call \'clear all\' again if you plan to now recompile a wrap\\n" "module, so that your recompiled module is used instead of the old one." << endl; std::cout.rdbuf(outbuf); + #ifdef GTSAM_WRAP_TBB_FINALIZE + // Terminate TBB worker threads deterministically before MATLAB unloads + // this MEX file. Otherwise the workers may still be winding down when + // the image is unmapped and touch freed code, which crashes on exit. + // See oneTBB issue #977. finalize() is idempotent, returns false rather + // than throwing if the scheduler is still referenced elsewhere, and TBB + // re-initializes transparently if the module is used again. + static tbb::task_scheduler_handle tbbHandle{{tbb::attach{{}}}}; + tbb::finalize(tbbHandle, std::nothrow); + #endif }} ''') diff --git a/gtwrap/matlab_wrapper/wrapper.py b/gtwrap/matlab_wrapper/wrapper.py index c5dd26b..324b994 100755 --- a/gtwrap/matlab_wrapper/wrapper.py +++ b/gtwrap/matlab_wrapper/wrapper.py @@ -1952,9 +1952,11 @@ def generate_wrapper(self, namespace): includes = textwrap.dedent("""\ {wrapper_file_headers} {boost_headers} + {tbb_headers} {includes_list} """).format(wrapper_file_headers=self.wrapper_file_headers.strip(), boost_headers=boost_headers, + tbb_headers=WrapperTemplate.tbb_headers, includes_list='\n'.join(map(str, includes_list))) preamble = self.generate_preamble() diff --git a/tests/expected/matlab/class_wrapper.cpp b/tests/expected/matlab/class_wrapper.cpp index 29d7156..d6b5327 100644 --- a/tests/expected/matlab/class_wrapper.cpp +++ b/tests/expected/matlab/class_wrapper.cpp @@ -1,6 +1,15 @@ #include #include + +#ifdef GTSAM_USE_TBB +#include +#if TBB_INTERFACE_VERSION >= 12060 +#include +#define GTSAM_WRAP_TBB_FINALIZE 1 +#endif +#endif + #include typedef Fun FunDouble; @@ -139,6 +148,16 @@ void _deleteAllObjects() "calling destructors, call 'clear all' again if you plan to now recompile a wrap\n" "module, so that your recompiled module is used instead of the old one." << endl; std::cout.rdbuf(outbuf); +#ifdef GTSAM_WRAP_TBB_FINALIZE + // Terminate TBB worker threads deterministically before MATLAB unloads + // this MEX file. Otherwise the workers may still be winding down when + // the image is unmapped and touch freed code, which crashes on exit. + // See oneTBB issue #977. finalize() is idempotent, returns false rather + // than throwing if the scheduler is still referenced elsewhere, and TBB + // re-initializes transparently if the module is used again. + static tbb::task_scheduler_handle tbbHandle{tbb::attach{}}; + tbb::finalize(tbbHandle, std::nothrow); +#endif } void _class_RTTIRegister() { diff --git a/tests/expected/matlab/enum_wrapper.cpp b/tests/expected/matlab/enum_wrapper.cpp index 71e8075..d92c77a 100644 --- a/tests/expected/matlab/enum_wrapper.cpp +++ b/tests/expected/matlab/enum_wrapper.cpp @@ -2,6 +2,15 @@ #include +#ifdef GTSAM_USE_TBB +#include +#if TBB_INTERFACE_VERSION >= 12060 +#include +#define GTSAM_WRAP_TBB_FINALIZE 1 +#endif +#endif + + typedef gtsam::Optimizer OptimizerGaussNewtonParams; @@ -44,6 +53,16 @@ void _deleteAllObjects() "calling destructors, call 'clear all' again if you plan to now recompile a wrap\n" "module, so that your recompiled module is used instead of the old one." << endl; std::cout.rdbuf(outbuf); +#ifdef GTSAM_WRAP_TBB_FINALIZE + // Terminate TBB worker threads deterministically before MATLAB unloads + // this MEX file. Otherwise the workers may still be winding down when + // the image is unmapped and touch freed code, which crashes on exit. + // See oneTBB issue #977. finalize() is idempotent, returns false rather + // than throwing if the scheduler is still referenced elsewhere, and TBB + // re-initializes transparently if the module is used again. + static tbb::task_scheduler_handle tbbHandle{tbb::attach{}}; + tbb::finalize(tbbHandle, std::nothrow); +#endif } void _enum_RTTIRegister() { diff --git a/tests/expected/matlab/functions_wrapper.cpp b/tests/expected/matlab/functions_wrapper.cpp index e076237..eeca4bd 100644 --- a/tests/expected/matlab/functions_wrapper.cpp +++ b/tests/expected/matlab/functions_wrapper.cpp @@ -2,6 +2,15 @@ #include +#ifdef GTSAM_USE_TBB +#include +#if TBB_INTERFACE_VERSION >= 12060 +#include +#define GTSAM_WRAP_TBB_FINALIZE 1 +#endif +#endif + + @@ -20,6 +29,16 @@ void _deleteAllObjects() "calling destructors, call 'clear all' again if you plan to now recompile a wrap\n" "module, so that your recompiled module is used instead of the old one." << endl; std::cout.rdbuf(outbuf); +#ifdef GTSAM_WRAP_TBB_FINALIZE + // Terminate TBB worker threads deterministically before MATLAB unloads + // this MEX file. Otherwise the workers may still be winding down when + // the image is unmapped and touch freed code, which crashes on exit. + // See oneTBB issue #977. finalize() is idempotent, returns false rather + // than throwing if the scheduler is still referenced elsewhere, and TBB + // re-initializes transparently if the module is used again. + static tbb::task_scheduler_handle tbbHandle{tbb::attach{}}; + tbb::finalize(tbbHandle, std::nothrow); +#endif } void _functions_RTTIRegister() { diff --git a/tests/expected/matlab/geometry_wrapper.cpp b/tests/expected/matlab/geometry_wrapper.cpp index 691fddb..5585f34 100644 --- a/tests/expected/matlab/geometry_wrapper.cpp +++ b/tests/expected/matlab/geometry_wrapper.cpp @@ -5,6 +5,15 @@ #include #include + +#ifdef GTSAM_USE_TBB +#include +#if TBB_INTERFACE_VERSION >= 12060 +#include +#define GTSAM_WRAP_TBB_FINALIZE 1 +#endif +#endif + #include #include @@ -43,6 +52,16 @@ void _deleteAllObjects() "calling destructors, call 'clear all' again if you plan to now recompile a wrap\n" "module, so that your recompiled module is used instead of the old one." << endl; std::cout.rdbuf(outbuf); +#ifdef GTSAM_WRAP_TBB_FINALIZE + // Terminate TBB worker threads deterministically before MATLAB unloads + // this MEX file. Otherwise the workers may still be winding down when + // the image is unmapped and touch freed code, which crashes on exit. + // See oneTBB issue #977. finalize() is idempotent, returns false rather + // than throwing if the scheduler is still referenced elsewhere, and TBB + // re-initializes transparently if the module is used again. + static tbb::task_scheduler_handle tbbHandle{tbb::attach{}}; + tbb::finalize(tbbHandle, std::nothrow); +#endif } void _geometry_RTTIRegister() { diff --git a/tests/expected/matlab/inheritance_wrapper.cpp b/tests/expected/matlab/inheritance_wrapper.cpp index cc82eda..1fae915 100644 --- a/tests/expected/matlab/inheritance_wrapper.cpp +++ b/tests/expected/matlab/inheritance_wrapper.cpp @@ -2,6 +2,15 @@ #include +#ifdef GTSAM_USE_TBB +#include +#if TBB_INTERFACE_VERSION >= 12060 +#include +#define GTSAM_WRAP_TBB_FINALIZE 1 +#endif +#endif + + typedef MyTemplate MyTemplatePoint2; typedef MyTemplate MyTemplateMatrix; @@ -87,6 +96,16 @@ void _deleteAllObjects() "calling destructors, call 'clear all' again if you plan to now recompile a wrap\n" "module, so that your recompiled module is used instead of the old one." << endl; std::cout.rdbuf(outbuf); +#ifdef GTSAM_WRAP_TBB_FINALIZE + // Terminate TBB worker threads deterministically before MATLAB unloads + // this MEX file. Otherwise the workers may still be winding down when + // the image is unmapped and touch freed code, which crashes on exit. + // See oneTBB issue #977. finalize() is idempotent, returns false rather + // than throwing if the scheduler is still referenced elsewhere, and TBB + // re-initializes transparently if the module is used again. + static tbb::task_scheduler_handle tbbHandle{tbb::attach{}}; + tbb::finalize(tbbHandle, std::nothrow); +#endif } void _inheritance_RTTIRegister() { diff --git a/tests/expected/matlab/multiple_files_wrapper.cpp b/tests/expected/matlab/multiple_files_wrapper.cpp index 00e141a..cb22e2d 100644 --- a/tests/expected/matlab/multiple_files_wrapper.cpp +++ b/tests/expected/matlab/multiple_files_wrapper.cpp @@ -2,6 +2,15 @@ #include +#ifdef GTSAM_USE_TBB +#include +#if TBB_INTERFACE_VERSION >= 12060 +#include +#define GTSAM_WRAP_TBB_FINALIZE 1 +#endif +#endif + + @@ -44,6 +53,16 @@ void _deleteAllObjects() "calling destructors, call 'clear all' again if you plan to now recompile a wrap\n" "module, so that your recompiled module is used instead of the old one." << endl; std::cout.rdbuf(outbuf); +#ifdef GTSAM_WRAP_TBB_FINALIZE + // Terminate TBB worker threads deterministically before MATLAB unloads + // this MEX file. Otherwise the workers may still be winding down when + // the image is unmapped and touch freed code, which crashes on exit. + // See oneTBB issue #977. finalize() is idempotent, returns false rather + // than throwing if the scheduler is still referenced elsewhere, and TBB + // re-initializes transparently if the module is used again. + static tbb::task_scheduler_handle tbbHandle{tbb::attach{}}; + tbb::finalize(tbbHandle, std::nothrow); +#endif } void _multiple_files_RTTIRegister() { diff --git a/tests/expected/matlab/namespaces_wrapper.cpp b/tests/expected/matlab/namespaces_wrapper.cpp index 2e327ec..c8992af 100644 --- a/tests/expected/matlab/namespaces_wrapper.cpp +++ b/tests/expected/matlab/namespaces_wrapper.cpp @@ -1,6 +1,15 @@ #include #include + +#ifdef GTSAM_USE_TBB +#include +#if TBB_INTERFACE_VERSION >= 12060 +#include +#define GTSAM_WRAP_TBB_FINALIZE 1 +#endif +#endif + #include #include #include @@ -81,6 +90,16 @@ void _deleteAllObjects() "calling destructors, call 'clear all' again if you plan to now recompile a wrap\n" "module, so that your recompiled module is used instead of the old one." << endl; std::cout.rdbuf(outbuf); +#ifdef GTSAM_WRAP_TBB_FINALIZE + // Terminate TBB worker threads deterministically before MATLAB unloads + // this MEX file. Otherwise the workers may still be winding down when + // the image is unmapped and touch freed code, which crashes on exit. + // See oneTBB issue #977. finalize() is idempotent, returns false rather + // than throwing if the scheduler is still referenced elsewhere, and TBB + // re-initializes transparently if the module is used again. + static tbb::task_scheduler_handle tbbHandle{tbb::attach{}}; + tbb::finalize(tbbHandle, std::nothrow); +#endif } void _namespaces_RTTIRegister() { diff --git a/tests/expected/matlab/special_cases_wrapper.cpp b/tests/expected/matlab/special_cases_wrapper.cpp index 1cc8579..47ab073 100644 --- a/tests/expected/matlab/special_cases_wrapper.cpp +++ b/tests/expected/matlab/special_cases_wrapper.cpp @@ -1,6 +1,15 @@ #include #include + +#ifdef GTSAM_USE_TBB +#include +#if TBB_INTERFACE_VERSION >= 12060 +#include +#define GTSAM_WRAP_TBB_FINALIZE 1 +#endif +#endif + #include typedef gtsam::PinholeCamera PinholeCameraCal3Bundler; @@ -53,6 +62,16 @@ void _deleteAllObjects() "calling destructors, call 'clear all' again if you plan to now recompile a wrap\n" "module, so that your recompiled module is used instead of the old one." << endl; std::cout.rdbuf(outbuf); +#ifdef GTSAM_WRAP_TBB_FINALIZE + // Terminate TBB worker threads deterministically before MATLAB unloads + // this MEX file. Otherwise the workers may still be winding down when + // the image is unmapped and touch freed code, which crashes on exit. + // See oneTBB issue #977. finalize() is idempotent, returns false rather + // than throwing if the scheduler is still referenced elsewhere, and TBB + // re-initializes transparently if the module is used again. + static tbb::task_scheduler_handle tbbHandle{tbb::attach{}}; + tbb::finalize(tbbHandle, std::nothrow); +#endif } void _special_cases_RTTIRegister() { diff --git a/tests/expected/matlab/template_wrapper.cpp b/tests/expected/matlab/template_wrapper.cpp index 057e26f..60394b6 100644 --- a/tests/expected/matlab/template_wrapper.cpp +++ b/tests/expected/matlab/template_wrapper.cpp @@ -2,6 +2,15 @@ #include +#ifdef GTSAM_USE_TBB +#include +#if TBB_INTERFACE_VERSION >= 12060 +#include +#define GTSAM_WRAP_TBB_FINALIZE 1 +#endif +#endif + + typedef ScopedTemplate ScopedTemplateResult; @@ -36,6 +45,16 @@ void _deleteAllObjects() "calling destructors, call 'clear all' again if you plan to now recompile a wrap\n" "module, so that your recompiled module is used instead of the old one." << endl; std::cout.rdbuf(outbuf); +#ifdef GTSAM_WRAP_TBB_FINALIZE + // Terminate TBB worker threads deterministically before MATLAB unloads + // this MEX file. Otherwise the workers may still be winding down when + // the image is unmapped and touch freed code, which crashes on exit. + // See oneTBB issue #977. finalize() is idempotent, returns false rather + // than throwing if the scheduler is still referenced elsewhere, and TBB + // re-initializes transparently if the module is used again. + static tbb::task_scheduler_handle tbbHandle{tbb::attach{}}; + tbb::finalize(tbbHandle, std::nothrow); +#endif } void _template_RTTIRegister() {