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);
+}

Reply via email to