OpenMP has some convenience templates for memory allocation:
std::vector<int, omp::allocator::cgroup_mem<int>> vec;
In OpenMP 6.1, this was extended to user-defined allocators,
which is syntax wise not as nice but still nicer than having
to coding it oneself:
std::vector<int, omp::allocator::user_mem<int>> vec (
{omp::allocator::user_mem<int>(my_alloc)} );
This patch adds it, see OpenMP Spec Issue 4731 / Pull Req. 4731
[the latter contains this/my patch and some further discussion].
Any comment before I push this to mainline?
Tobias
libgomp: Add omp::allocator::user_mem template to omp.h
libgomp/ChangeLog:
* omp.h.in: Add omp::allocator::user_mem template.
* libgomp.texi (Memory allocation): Add example.
(Impl. status): Mark as implemented.
* testsuite/libgomp.c++/user_mem.C: New test.
libgomp/libgomp.texi | 14 +++++++++++++-
libgomp/omp.h.in | 30 ++++++++++++++++++++++++++++++
libgomp/testsuite/libgomp.c++/user_mem.C | 32 ++++++++++++++++++++++++++++++++
3 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi
index 14054c5e6be..f4d6ab71e0b 100644
--- a/libgomp/libgomp.texi
+++ b/libgomp/libgomp.texi
@@ -716,7 +716,7 @@ to address of matching mapped list item per 5.1, Sect. 2.21.7.2 @tab N @tab
@item New @code{omp_default_device} identifier as conforming device number
@tab Y @tab
@item @code{user_mem} class template added to the @code{omp::allocator} namespace
- @tab N @tab
+ @tab Y @tab See also @ref{Memory allocation}
@item @code{omp-tools.h} providing types to avoid @code{omp.h} for tools
@tab N @tab
@item @code{omp_get_filtered_devices} API routines @tab N @tab
@@ -7837,6 +7837,18 @@ The following allocator templates are supported:
@item ompx_gnu_managed_mem_alloc @tab ompx::allocator::gnu_managed_mem
@end multitable
+For user-defined allocators, the class template @code{user_mem} can
+be used as in the following example:
+
+@smallexample
+omp_allocator_handle_t my_alloc /* = omp_init_allocator (...) */ ;
+
+omp::allocator::user_mem<int> my_alloc_class(my_alloc);
+std::vector<int, decltype (my_alloc_class)> vec1 (my_alloc_class);
+
+std::vector<int, omp::allocator::user_mem<int>> vec2 (@{omp::allocator::user_mem<int>(my_alloc)@});
+@end smallexample
+
The following traits are available when constructing a new allocator;
if a trait is not specified or with the value @code{default}, the
specified default value is used for that trait. The predefined
diff --git a/libgomp/omp.h.in b/libgomp/omp.h.in
index 1f7b12ea710..b4d2c8acb29 100644
--- a/libgomp/omp.h.in
+++ b/libgomp/omp.h.in
@@ -593,6 +593,36 @@ template <typename __T>
struct thread_mem
: __detail::__allocator_templ <__T, omp_thread_mem_alloc> {};
+template <typename __T>
+struct user_mem
+{
+ using value_type = __T;
+ using pointer = __T *;
+ using const_pointer = const __T *;
+ using size_type = __SIZE_TYPE__;
+ using difference_type = __PTRDIFF_TYPE__;
+
+ omp_allocator_handle_t my_alloc;
+
+ user_mem(omp_allocator_handle_t h) : my_alloc(h) { }
+ __T *
+ allocate (size_type __n)
+ {
+ if (__SIZE_MAX__ / sizeof (__T) < __n)
+ std::__throw_bad_array_new_length ();
+ void *__p = omp_aligned_alloc (alignof(__T), __n * sizeof(__T), my_alloc);
+ if (!__p)
+ std::__throw_bad_alloc ();
+ return static_cast <__T *> (__p);
+ }
+
+ void
+ deallocate (__T *__p, size_type) __GOMP_NOTHROW
+ {
+ omp_free (static_cast <void *> (__p), my_alloc);
+ }
+};
+
} /* namespace allocator */
} /* namespace omp */
diff --git a/libgomp/testsuite/libgomp.c++/user_mem.C b/libgomp/testsuite/libgomp.c++/user_mem.C
new file mode 100644
index 00000000000..e3b336f9e7e
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/user_mem.C
@@ -0,0 +1,32 @@
+#include <assert.h>
+#include <vector>
+#include <omp.h>
+
+
+int
+main ()
+{
+ const omp_alloctrait_t traits[] = { { omp_atk_alignment, 1024 } };
+
+ omp_allocator_handle_t my_alloc;
+
+ my_alloc = omp_init_allocator (omp_default_mem_space, 1, traits);
+ assert (my_alloc != omp_null_allocator);
+
+ omp::allocator::user_mem<int> alloc_class(my_alloc);
+
+ std::vector<int, omp::allocator::user_mem<int>> vec1(alloc_class);
+ assert (vec1.empty());
+ vec1.push_back(1);
+ assert (((omp_uintptr_t) &vec1[0]) % 1024 == 0);
+
+ std::vector<int, decltype (alloc_class)> vec2(alloc_class);
+ assert (vec2.empty());
+ vec2.push_back(1);
+ assert (((omp_uintptr_t) &vec2[0]) % 1024 == 0);
+
+ std::vector<int, omp::allocator::user_mem<int>> vec3({omp::allocator::user_mem<int>(my_alloc)});
+ assert (vec3.empty());
+ vec3.push_back(5);
+ assert (((omp_uintptr_t) &vec3[0]) % 1024 == 0);
+}