Skip to content

Commit

Permalink
Fix segfault due to mxArray internals change #29
Browse files Browse the repository at this point in the history
  • Loading branch information
mducle committed Oct 3, 2024
1 parent 81f5cc7 commit 07578c6
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 14 deletions.
4 changes: 2 additions & 2 deletions src/libpymcr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,12 @@ namespace libpymcr {

// Constructor
matlab_env::matlab_env(const std::u16string ctfname, std::string matlabroot, std::vector<std::u16string> options) {
_loadlibraries(matlabroot);
_MLVER = _loadlibraries(matlabroot);
auto mode = matlab::cpplib::MATLABApplicationMode::IN_PROCESS;
// Specify MATLAB startup options
_app = matlab::cpplib::initMATLABApplication(mode, options);
_lib = matlab::cpplib::initMATLABLibrary(_app, ctfname);
_converter = pymat_converter(pymat_converter::NumpyConversion::COPY);
_converter = pymat_converter(pymat_converter::NumpyConversion::WRAP, _MLVER);
_in_evaluation = false;
}

Expand Down
1 change: 1 addition & 0 deletions src/libpymcr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ namespace libpymcr {
size_t _parse_inputs(std::vector<matlab::data::Array>& m_args, py::args py_args, py::kwargs& py_kwargs);
template <class T> T evalloop(matlab::cpplib::FutureResult<T> resAsync);
bool _in_evaluation;
double _MLVER;
public:
py::object feval(const std::u16string &funcname, py::args args, py::kwargs& kwargs);
py::object call(py::args args, py::kwargs& kwargs);
Expand Down
7 changes: 5 additions & 2 deletions src/load_matlab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,15 @@ std::string _getMLversion(std::string mlroot) {
}
return _MLVERSTR;
}
void _loadlibraries(std::string matlabroot) {
double _loadlibraries(std::string matlabroot) {
std::string mlver = _getMLversion(matlabroot);
if (!_LIBCPPSHARED) {
_LIBDATAARRAY = _loadlib(matlabroot + "/extern/bin/", "libMatlabDataArray");
_LIBCPPSHARED = _loadlib(matlabroot + "/runtime/", "libMatlabCppSharedLib", _getMLversion(matlabroot));
_LIBCPPSHARED = _loadlib(matlabroot + "/runtime/", "libMatlabCppSharedLib", mlver);
_LIBMEX = _loadlib(matlabroot + "/bin/", "libmex");
}
char *end;
return std::strtod(mlver.c_str(), &end);
}
void _checklibs() {
if (!_LIBDATAARRAY || !_LIBCPPSHARED || !_LIBMEX) {
Expand Down
2 changes: 1 addition & 1 deletion src/load_matlab.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
void *_loadlib(std::string path, const char* libname, std::string mlver="");
void *_resolve(void* lib, const char* sym);
std::string _getMLversion(std::string mlroot);
void _loadlibraries(std::string matlabroot);
double _loadlibraries(std::string matlabroot);

void* mexGetFunctionImpl();
void mexDestroyFunctionImpl(void* impl);
Expand Down
22 changes: 15 additions & 7 deletions src/type_converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ void matlab_wrapper_del(PyObject* pyself) {
}

// Functions to handle Matlab mxArrays
struct mxArray_header_2020a* _get_mxArray(Array arr) {
struct mxArray_header_2020a* _get_mxArray(Array arr, double mlver) {
matlab::data::impl::ArrayImpl* imp = reinterpret_cast<mArray*>(&arr)->get_ptr();
struct impl_header_col_major* m0 = reinterpret_cast<struct impl_header_col_major*>(imp);
struct impl_header_col_major* m1 = reinterpret_cast<struct impl_header_col_major*>(m0->data_ptr);
#if defined __APPLE__
return reinterpret_cast<struct mxArray_header_2020a*>(m1->data_ptr);
#else
return reinterpret_cast<struct mxArray_header_2020a*>(m1->mxArray);
if (mlver > 10) { // R2023b or newer
return reinterpret_cast<struct mxArray_header_2020a*>(m1->mxArray2);
} else {
return reinterpret_cast<struct mxArray_header_2020a*>(m1->mxArray);
}
#endif
}

Expand All @@ -43,9 +47,9 @@ PyObject* pymat_converter::is_wrapped_np_data(void *addr) {
return nullptr;
}

void* _get_data_pointer(matlab::data::Array arr) {
void* _get_data_pointer(matlab::data::Array arr, double mlver) {
if (arr.getMemoryLayout() == matlab::data::MemoryLayout::COLUMN_MAJOR) {
struct mxArray_header_2020a* mx = _get_mxArray(arr);
struct mxArray_header_2020a* mx = _get_mxArray(arr, mlver);
return mx->pr;
} else {
matlab::data::impl::ArrayImpl* imp = reinterpret_cast<mArray*>(&arr)->get_ptr();
Expand All @@ -58,7 +62,7 @@ void* _get_data_pointer(matlab::data::Array arr) {
template <typename T> PyObject* pymat_converter::matlab_to_python_t (matlab::data::Array arr, dt<T>) {
// First checks if the array is not constructed from numpy data in the first place
if (m_numpy_conv_flag == NumpyConversion::WRAP) {
PyObject* wrapper = is_wrapped_np_data(_get_data_pointer(arr));
PyObject* wrapper = is_wrapped_np_data(_get_data_pointer(arr, m_MLVERSION));
if (wrapper != nullptr) {
// If so, just return the original numpy array, but need to INCREF it as returning new reference
if (!m_mex_flag) {
Expand Down Expand Up @@ -287,6 +291,9 @@ template <typename T> Array pymat_converter::raw_to_matlab(char *raw, size_t sz,

bool pymat_converter::release_buffer(matlab::data::Array arr) {
// Hack to safely release a buffer for a no-copy Matlab array converted from numpy
if (m_MLVERSION > 24.1) { // R2024b or newer supports custom deleter so don't need hack
return true;
}
if (m_numpy_conv_flag == NumpyConversion::COPY) {
return true;
}
Expand All @@ -300,7 +307,7 @@ bool pymat_converter::release_buffer(matlab::data::Array arr) {
// to point to this instead of the numpy array. Then when this array is deleted Matlab will
// free the newly created buffer instead of the numpy array which causes a heap memory error
matlab::data::ArrayFactory factory;
struct mxArray_header_2020a* mx = _get_mxArray(arr);
struct mxArray_header_2020a* mx = _get_mxArray(arr, m_MLVERSION);
long rc = (mx->refcount == nullptr) ? 1 : *(mx->refcount);
if (mx->refcount == nullptr || *(mx->refcount) == 1) {
buffer_ptr_t<double> buf = factory.createBuffer<double>(1);
Expand Down Expand Up @@ -585,7 +592,8 @@ matlab::data::Array pymat_converter::to_matlab(PyObject *input, bool mex_flag) {
return python_to_matlab_single(input, factory);
}

pymat_converter::pymat_converter(NumpyConversion np_behaviour) : m_numpy_conv_flag(np_behaviour) {
pymat_converter::pymat_converter(NumpyConversion np_behaviour, double matlab_version)
: m_numpy_conv_flag(np_behaviour), m_MLVERSION(matlab_version) {
m_py_matlab_wrapper_t = (PyTypeObject*) PyType_FromSpec(&spec_matlab_wrapper);
}

Expand Down
8 changes: 6 additions & 2 deletions src/type_converter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ namespace libpymcr {
void *unknown3;
void *unknown4;
#endif
void *mxArray; // Pointer to mxArray in the *data_ptr struct
void *mxArray; // Pointer to mxArray in the *data_ptr struct (R2020a-R2023a)
void *ptr1; // Unknown function pointer in >R2023b (deleter?)
void *ptr2; // Unknown function pointer in >R2023b
void *mxArray2; // Pointer to mxArray in >R2023b
};

// Header for a (new-style) row-major array
Expand Down Expand Up @@ -143,6 +146,7 @@ namespace libpymcr {
std::vector< std::pair<matlab::data::Array, PyObject*> > m_py_cache;
PyTypeObject* m_py_matlab_wrapper_t;
bool m_mex_flag;
double m_MLVERSION;
// Methods to convert from Matlab to Python
PyObject* is_wrapped_np_data(void* addr);
template <typename T> PyObject* matlab_to_python_t (matlab::data::Array arr, dt<T>);
Expand All @@ -166,7 +170,7 @@ namespace libpymcr {
matlab::data::Array python_to_matlab_single(PyObject *input, matlab::data::ArrayFactory &factory);
public:
void clear_py_cache();
pymat_converter(NumpyConversion np_behaviour=NumpyConversion::COPY);
pymat_converter(NumpyConversion np_behaviour=NumpyConversion::COPY, double matlab_version=9.8);
~pymat_converter();
matlab::data::Array to_matlab(PyObject *input, bool mex_flag=false);
PyObject* to_python(matlab::data::Array input);
Expand Down

0 comments on commit 07578c6

Please sign in to comment.