Mercurial > octave
changeset 30488:abb4823df535 stable
with C++17, match malloc/free for MEX memory (bug #61472)
* Array-fwd.h, Array.h: If std::pmr::polymorphic_allocator<T> is
available, use it as the default allocator for the Array<T> class.
* mex.cc (fp_type_traits): New traits class and specializations.
(mx_memory_resource): If std::pmr::polymorphic_allocator<T> is
available, define class to manage MEX memory allocations when they are
transferred to octave_value objects.
(the_mx_memory_resource): New static object.
(fp_to_ov): Use resource-aquiring Array constructor with a
pointer to the_mx_memory_resource so that the Array object will own
the allocated array and use std::free to release the storage storage
allocated with mxMalloc or mxRealloc after it is returned to Octave
in an octave_value object. If the polymorphic allocator is not
availble (for example, compiling with a pre-c++17 compiler) then don't
mix malloc with delete[] for arrays of Complex or FloatComplex objects.
(int_to_ov): Set up to use the same kind of resource management as for
fp_to_ov, except disable for now (we need to extend the intNDArray
classes to have resource-acquiring constructors).
* acinclude.m4 (OCTAVE_CHECK_STD_PMR_POLYMORPHIC_ALLOCATOR):
New macro.
* configure.ac: Use it.
* mk-octave-config-h.sh: Insert definition for
OCTAVE_CHECK_STD_PMR_POLYMORPHIC_ALLOCATOR into octave-config.h.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Sat, 04 Dec 2021 09:13:12 -0500 |
parents | 1f51aa6719e5 |
children | bd67d0045e21 |
files | build-aux/mk-octave-config-h.sh configure.ac libinterp/corefcn/mex.cc liboctave/array/Array-fwd.h liboctave/array/Array.h m4/acinclude.m4 |
diffstat | 6 files changed, 193 insertions(+), 19 deletions(-) [+] |
line wrap: on
line diff
--- a/build-aux/mk-octave-config-h.sh Wed Dec 15 16:22:35 2021 -0500 +++ b/build-aux/mk-octave-config-h.sh Sat Dec 04 09:13:12 2021 -0500 @@ -279,6 +279,7 @@ $SED -n 's/#\(\(undef\|define\) OCTAVE_ENABLE_OPENMP.*$\)/# \1/p' $config_h_file $SED -n 's/#\(\(undef\|define\) OCTAVE_HAVE_LONG_LONG_INT.*$\)/# \1/p' $config_h_file $SED -n 's/#\(\(undef\|define\) OCTAVE_HAVE_UNSIGNED_LONG_LONG_INT.*$\)/# \1/p' $config_h_file +$SED -n 's/#\(\(undef\|define\) OCTAVE_HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR.*$\)/# \1/p' $config_h_file $SED -n 's/#\(\(undef\|define\) OCTAVE_HAVE_OVERLOAD_CHAR_INT8_TYPES.*$\)/# \1/p' $config_h_file $SED -n 's/#\(\(undef\|define\) OCTAVE_SIZEOF_F77_INT_TYPE.*$\)/# \1/p' $config_h_file $SED -n 's/#\(\(undef\|define\) OCTAVE_SIZEOF_IDX_TYPE.*$\)/# \1/p' $config_h_file
--- a/configure.ac Wed Dec 15 16:22:35 2021 -0500 +++ b/configure.ac Sat Dec 04 09:13:12 2021 -0500 @@ -400,6 +400,8 @@ OCTAVE_CONFIGURE_WARNING([warn_stl_algo_h]) fi +OCTAVE_CHECK_STD_PMR_POLYMORPHIC_ALLOCATOR + ### Check version number when using gcc. dnl It might be different from the g++ version number.
--- a/libinterp/corefcn/mex.cc Wed Dec 15 16:22:35 2021 -0500 +++ b/libinterp/corefcn/mex.cc Sat Dec 04 09:13:12 2021 -0500 @@ -27,6 +27,12 @@ # include "config.h" #endif +#define DEBUG 1 + +#if defined (DEBUG) +# include <iostream> +#endif + #include <cstdarg> #include <cstdlib> #include <cstring> @@ -34,9 +40,12 @@ #include <limits> #include <map> +#include <memory_resource> #include <set> #include <string> +// Needed to instantiate Array objects with custom allocator. +#include "Array.cc" #include "f77-fcn.h" #include "lo-ieee.h" #include "oct-locbuf.h" @@ -253,18 +262,12 @@ extern OCTINTERP_API void mxSetImagData (mxArray *ptr, void *pi); } -// #define DEBUG 1 - -#if DEBUG -# include <iostream> -#endif - static void * xmalloc (size_t n) { void *ptr = std::malloc (n); -#if DEBUG +#if defined (DEBUG) std::cerr << "xmalloc (" << n << ") = " << ptr << std::endl; #endif @@ -276,7 +279,7 @@ { void *newptr = std::realloc (ptr, n); -#if DEBUG +#if defined (DEBUG) std::cerr << "xrealloc (" << ptr << ", " << n << ") = " << newptr << std::endl; #endif @@ -287,7 +290,7 @@ static void xfree (void *ptr) { -#if DEBUG +#if defined (DEBUG) std::cerr << "xfree (" << ptr << ")" << std::endl; #endif @@ -310,6 +313,63 @@ return max_len; } +// FIXME: Is there a better/standard way to do this job? + +template <typename T> +class fp_type_traits +{ +public: + static const bool is_complex = false; +}; + +template <> +class fp_type_traits<Complex> +{ +public: + static const bool is_complex = true; +}; + +template <> +class fp_type_traits <FloatComplex> +{ +public: + static const bool is_complex = true; +}; + +#if defined (HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR) +# include <memory_resource> + +class mx_memory_resource : public std::pmr::memory_resource +{ +private: + + void * do_allocate (std::size_t bytes, size_t /*alignment*/) + { + void *ptr = xmalloc (bytes); + + if (! ptr) + throw std::bad_alloc (); + + return ptr; + } + + void do_deallocate (void* ptr, std::size_t /*bytes*/, + std::size_t /*alignment*/) + { + xfree (ptr); + } + + bool do_is_equal (const std::pmr::memory_resource& other) const noexcept + { + return this == dynamic_cast<const mx_memory_resource *> (&other); + } +}; + +// FIXME: Is it OK for the memory resource object to be defined this +// way? +static mx_memory_resource the_mx_memory_resource; +#endif + // ------------------------------------------------------------------ mxArray_base::mxArray_base (bool interleaved) @@ -2076,13 +2136,46 @@ octave_value fp_to_ov (const dim_vector& dv) const { + octave_value retval; + ELT_T *ppr = static_cast<ELT_T *> (m_pr); - Array<ELT_T> val (ppr, dv); - - maybe_disown_ptr (m_pr); - - return octave_value (val); +#if defined (HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR) + + octave::unwind_action act ([=] () { maybe_disown_ptr (m_pr); }); + + return octave_value (Array<ELT_T> (ppr, dv, &the_mx_memory_resource)); + +#else + + if (fp_type_traits<ELT_T>::is_complex) + { + // Mixing malloc and delete[] for arrays of Complex and + // FloatComplex objects is not possible. + + Array<ELT_T> val (dv); + + ELT_T *ptr = val.fortran_vec (); + + mwSize nel = get_number_of_elements (); + + for (mwIndex i = 0; i < nel; i++) + ptr[i] = ppr[i]; + + return octave_value (val); + } + else + { + // Although behavior is not specified by the standard, it should + // work to mix malloc and delete[] for arrays of float and + // double. + + octave::unwind_action act ([=] () { maybe_disown_ptr (m_pr); }); + + return octave_value (Array<ELT_T> (ppr, dv)); + } + +#endif } template <typename ELT_T, typename ARRAY_T, typename ARRAY_ELT_T> @@ -2094,11 +2187,17 @@ ELT_T *ppr = static_cast<ELT_T *> (m_pr); -#if 0 - ARRAY_T val (ppr, dv); - - maybe_disown_ptr (m_pr); +#if 0 && defined (HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR) + + octave::unwind_action act ([=] () { maybe_disown_ptr (m_pr); }); + + return ARRAY_T (ppr, dv, &the_mx_memory_resource); + #else + + // All octave_int types are objects so we can't mix malloc and + // delete[] and we always have to copy. + ARRAY_T val (dv); ARRAY_ELT_T *ptr = val.fortran_vec (); @@ -2107,9 +2206,10 @@ for (mwIndex i = 0; i < nel; i++) ptr[i] = ppr[i]; -#endif return octave_value (val); + +#endif } protected: @@ -2360,6 +2460,9 @@ T *ppr = static_cast<T *> (m_pr); + // We allocate in the Array<T> constructor and copy here, so we + // don't need the custom allocator for this object. + Array<std::complex<T>> val (dv); std::complex<T> *ptr = val.fortran_vec ();
--- a/liboctave/array/Array-fwd.h Wed Dec 15 16:22:35 2021 -0500 +++ b/liboctave/array/Array-fwd.h Sat Dec 04 09:13:12 2021 -0500 @@ -28,7 +28,17 @@ #include "octave-config.h" +#if defined (HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR) +# include <memory_resource> + +template <typename T, typename Alloc = std::pmr::polymorphic_allocator<T>> +class OCTARRAY_API Array; + +#else + template <typename T, typename Alloc = std::allocator<T>> class OCTARRAY_API Array; #endif + +#endif
--- a/liboctave/array/Array.h Wed Dec 15 16:22:35 2021 -0500 +++ b/liboctave/array/Array.h Sat Dec 04 09:13:12 2021 -0500 @@ -789,7 +789,12 @@ //! Apply function fcn to each element of the Array<T, Alloc>. This function //! is optimized with a manually unrolled loop. +#if defined (HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR) + template <typename U, typename F, + typename A = std::pmr::polymorphic_allocator<U>> +#else template <typename U, typename F, typename A = std::allocator<U>> +#endif Array<U, A> map (F fcn) const { @@ -821,12 +826,20 @@ //@{ //! Overloads for function references. +#if defined (HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR) + template <typename U, typename A = std::pmr::polymorphic_allocator<U>> +#else template <typename U, typename A = std::allocator<U>> +#endif Array<U, A> map (U (&fcn) (T)) const { return map<U, U (&) (T), A> (fcn); } +#if defined (HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR) + template <typename U, typename A = std::pmr::polymorphic_allocator<U>> +#else template <typename U, typename A = std::allocator<U>> +#endif Array<U, A> map (U (&fcn) (const T&)) const { return map<U, U (&) (const T&), A> (fcn); }
--- a/m4/acinclude.m4 Wed Dec 15 16:22:35 2021 -0500 +++ b/m4/acinclude.m4 Sat Dec 04 09:13:12 2021 -0500 @@ -255,6 +255,51 @@ fi ]) dnl +dnl Check whether std::pmr::polymorphic_allocator is available. +dnl +AC_DEFUN([OCTAVE_CHECK_STD_PMR_POLYMORPHIC_ALLOCATOR], [ + AC_CACHE_CHECK([whether std::pmr::polymorphic_allocator is avalable], + [octave_cv_std_pmr_polymorphic_allocator], + [AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include <cstdlib> + #include <memory_resource> + #include <vector> + class mx_memory_resource : public std::pmr::memory_resource + { + private: + void * do_allocate (std::size_t bytes, size_t /*alignment*/) + { + void *ptr = std::malloc (bytes); + if (! ptr) + throw std::bad_alloc (); + return ptr; + } + void do_deallocate (void* ptr, std::size_t /*bytes*/, + std::size_t /*alignment*/) + { + std::free (ptr); + } + bool do_is_equal (const std::pmr::memory_resource& other) const noexcept + { + return this == dynamic_cast<const mx_memory_resource *> (&other); + return true; + } + }; + mx_memory_resource the_mx_memory_resource; + ]], [[ + std::pmr::vector<int> my_int_vec { &the_mx_memory_resource }; + ]])], + octave_cv_std_pmr_polymorphic_allocator=yes, + octave_cv_std_pmr_polymorphic_allocator=no) + AC_LANG_POP(C++) + ]) + if test $octave_cv_std_pmr_polymorphic_allocator = yes; then + AC_DEFINE(HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR, 1, + [Define to 1 if std::pmr::polymorphic_allocator is available.]) + fi +]) +dnl dnl Check whether CXSparse is version 2.2 or later dnl FIXME: This test uses a version number. It potentially could dnl be re-written to actually call a function, but is it worth it?