opm-common
CopyablePtr.hpp
1 /*
2  Copyright 2022 Equinor ASA.
3 
4  This file is part of the Open Porous Media project (OPM).
5 
6  OPM is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  OPM is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with OPM. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #ifndef OPM_COPYABLE_PTR_HPP
21 #define OPM_COPYABLE_PTR_HPP
22 
23 #include <memory>
24 #include <type_traits>
25 #include <utility>
26 #include <opm/common/utility/gpuDecorators.hpp>
27 
28 namespace Opm {
29 namespace Utility {
30 
31 // Wraps a raw pointer and makes it copyable, with GPU support.
32 //
33 // On the host: owns the pointed-to object (deep copies on copy-construct/assign,
34 // destroys on destruction) — same semantics as the original unique_ptr wrapper.
35 // On the device (CUDA/HIP kernel): behaves as a non-owning view; copy/assign
36 // simply copy the raw pointer and the destructor is a no-op.
37 template <class T, bool on_gpu = OPM_IS_INSIDE_DEVICE_FUNCTION>
38 class CopyablePtr {
39 public:
40  template <class U, bool B>
41  friend class CopyablePtr;
42 
43  using ptr_type = std::conditional_t<on_gpu, T*, std::unique_ptr<T>>;
44 
45  // WARNING: This template should not be used with polymorphic classes.
46  // That would require a virtual clone() method. It will only ever copy
47  // the static class type of the pointed-to object.
48  static_assert(!std::is_polymorphic_v<T>,
49  "CopyablePtr does not support polymorphic types");
50 
51  OPM_HOST_DEVICE CopyablePtr() : ptr_(nullptr) {}
52 
53  OPM_HOST_DEVICE CopyablePtr(const CopyablePtr& other) {
54  if constexpr (on_gpu) {
55  ptr_ = other.ptr_;
56  } else {
57  ptr_ = other ? std::make_unique<T>(*other.get()) : nullptr;
58  }
59  }
60 
61  OPM_HOST_DEVICE CopyablePtr(CopyablePtr&& other) noexcept {
62  if constexpr (on_gpu) {
63  ptr_ = other.ptr_;
64  other.ptr_ = nullptr;
65  } else {
66  ptr_ = std::move(other.ptr_);
67  }
68  }
69 
70  // copy assignment
71  OPM_HOST_DEVICE CopyablePtr& operator=(const CopyablePtr& other) {
72  if (this != &other) {
73  if constexpr (on_gpu) {
74  ptr_ = other.ptr_;
75  } else {
76  ptr_ = other ? std::make_unique<T>(*other.get()) : nullptr;
77  }
78  }
79  return *this;
80  }
81 
82  // move assignment
83  OPM_HOST_DEVICE CopyablePtr& operator=(CopyablePtr&& other) noexcept {
84  if (this != &other) {
85  if constexpr (on_gpu) {
86  ptr_ = other.ptr_;
87  other.ptr_ = nullptr;
88  } else {
89  ptr_ = std::move(other.ptr_);
90  }
91  }
92  return *this;
93  }
94 
95  // Assign directly from a unique_ptr.
96  //
97  // This overload is host-only when on_gpu == true: the GPU-view
98  // configuration has a no-op destructor, so taking ownership of a
99  // \c unique_ptr<T> from the host would leak the object. We therefore
100  // delete this overload in the host pass for GPU-view instantiations
101  // (so user code can only construct GPU views from non-owning raw
102  // pointers via \c to_gpu_view()).
103  //
104  // In a device-compiler pass (CUDA/HIP) we must still allow the
105  // overload to compile, because host-only code that is transitively
106  // instantiated for the device pass (e.g. \c FlowProblem::updateRelperms
107  // with directional mobilities) names it. Such code is never executed
108  // on the device, so the historical \c release()-and-leak fallback is
109  // harmless there.
110 #if OPM_IS_INSIDE_DEVICE_FUNCTION
111  CopyablePtr& operator=(std::unique_ptr<T>&& uptr) {
112  if constexpr (on_gpu) {
113  ptr_ = uptr.release();
114  } else {
115  ptr_ = std::move(uptr);
116  }
117  return *this;
118  }
119 #else
120  CopyablePtr& operator=(std::unique_ptr<T>&& uptr)
121  requires (!on_gpu)
122  {
123  ptr_ = std::move(uptr);
124  return *this;
125  }
126 #endif
127 
128  OPM_HOST_DEVICE ~CopyablePtr() = default;
129 
130  // Deep equality: both unset, or both set with equal pointees.
131  OPM_HOST_DEVICE bool operator==(const CopyablePtr& other) const {
132  const T* lhs = deref_ptr();
133  const T* rhs = other.deref_ptr();
134 
135  if ((lhs == nullptr) != (rhs == nullptr)) {
136  return false;
137  }
138 
139  return (lhs == nullptr) || (*lhs == *rhs);
140  }
141 
142  OPM_HOST_DEVICE bool operator!=(const CopyablePtr& other) const {
143  return !(*this == other);
144  }
145 
146  // Serialise the owned object (host configuration only).
147  template <class Serializer>
148  void serializeOp(Serializer& serializer) {
149  if constexpr (!on_gpu) {
150  serializer(ptr_);
151  }
152  }
153 
154  // member access operator
155  OPM_HOST_DEVICE T* operator->() const { return deref_ptr(); }
156 
157  // boolean context operator
158  OPM_HOST_DEVICE explicit operator bool() const noexcept { return deref_ptr() != nullptr; }
159 
160  // get a raw pointer to the stored value
161  OPM_HOST_DEVICE T* get() const { return deref_ptr(); }
162 
163  // release ownership
164  OPM_HOST_DEVICE T* release() {
165  if constexpr (on_gpu) {
166  T* tmp = ptr_;
167  ptr_ = nullptr;
168  return tmp;
169  } else {
170  return ptr_.release();
171  }
172  }
173 
174  // Build a non-owning GPU view of the stored pointer.
175  OPM_HOST_DEVICE CopyablePtr<T, true> to_gpu_view() const {
176  return CopyablePtr<T, true>(this->get());
177  }
178 
179 private:
180  explicit OPM_HOST_DEVICE CopyablePtr(T* ptr) : ptr_(ptr) {}
181 
182  OPM_HOST_DEVICE T* deref_ptr() const {
183  if constexpr (on_gpu) {
184  return ptr_;
185  } else {
186  return ptr_.get();
187  }
188  }
189 
190  ptr_type ptr_;
191 };
192 
193 } // namespace Utility
194 } // namespace Opm
195 #endif
Definition: CopyablePtr.hpp:38
This class implements a small container which holds the transmissibility mulitpliers for all the face...
Definition: Exceptions.hpp:30
Class for (de-)serializing.
Definition: Serializer.hpp:95