1 /*
2  *  Copyright 2018 NVIDIA Corporation
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  */
16 
17 /*! \file mr/memory_resource.h
18  *  \brief A base class for the memory resource system, similar to std::memory_resource,
19  *      and related utilities.
20  */
21 
22 #pragma once
23 
24 #include "detail/config.h"
25 #ifdef THRUST_MR_STD_MR_HEADER
26 #  include THRUST_MR_STD_MR_HEADER
27 #endif
28 
29 namespace thrust
30 {
31 /*! \brief \p thrust::mr is the namespace containing system agnostic types and functions for \p memory_resource related functionalities.
32  */
33 namespace mr
34 {
35 
36 /*! \addtogroup memory_management Memory Management
37  *  \addtogroup memory_management_classes Memory Management Classes
38  *  \addtogroup memory_resources Memory Resources
39  *  \ingroup memory_management
40  *  \{
41  */
42 
43 /*! \p memory_resource is the base class for all other memory resources.
44  *
45  *  \tparam Pointer the pointer type that is allocated and deallocated by the memory resource
46  *      derived from this base class. If this is <tt>void *</tt>, this class derives from
47  *      <tt>std::pmr::memory_resource</tt>.
48  */
49 template<typename Pointer = void *>
50 class memory_resource
51 {
52 public:
53     /*! Alias for the template parameter.
54      */
55     typedef Pointer pointer;
56 
57     /*! Virtual destructor, defaulted when possible.
58      */
~memory_resource()59     virtual ~memory_resource() THRUST_DEFAULT
60 
61     /*! Allocates memory of size at least \p bytes and alignment at least \p alignment.
62      *
63      *  \param bytes size, in bytes, that is requested from this allocation
64      *  \param alignment alignment that is requested from this allocation
65      *  \throws thrust::bad_alloc when no memory with requested size and alignment can be allocated.
66      *  \returns A pointer to void to the newly allocated memory.
67      */
68     THRUST_NODISCARD
69     pointer allocate(std::size_t bytes, std::size_t alignment = THRUST_MR_DEFAULT_ALIGNMENT)
70     {
71         return do_allocate(bytes, alignment);
72     }
73 
74     /*! Deallocates memory pointed to by \p p.
75      *
76      *  \param p pointer to be deallocated
77      *  \param bytes the size of the allocation. This must be equivalent to the value of \p bytes that
78      *      was passed to the allocation function that returned \p p.
79      *  \param alignment the alignment of the allocation. This must be equivalent to the value of \p alignment
80      *      that was passed to the allocation function that returned \p p.
81      */
82     void deallocate(pointer p, std::size_t bytes, std::size_t alignment = THRUST_MR_DEFAULT_ALIGNMENT)
83     {
84         do_deallocate(p, bytes, alignment);
85     }
86 
87     /*! Compares this resource to the other one. The default implementation uses identity comparison,
88      *      which is often the right thing to do and doesn't require RTTI involvement.
89      *
90      *  \param other the other resource to compare this resource to
91      *  \returns whether the two resources are equivalent.
92      */
93     __host__ __device__
is_equal(const memory_resource & other)94     bool is_equal(const memory_resource & other) const THRUST_NOEXCEPT
95     {
96         return do_is_equal(other);
97     }
98 
99     /*! Allocates memory of size at least \p bytes and alignment at least \p alignment.
100      *
101      *  \param bytes size, in bytes, that is requested from this allocation
102      *  \param alignment alignment that is requested from this allocation
103      *  \throws thrust::bad_alloc when no memory with requested size and alignment can be allocated.
104      *  \returns A pointer to void to the newly allocated memory.
105      */
106     virtual pointer do_allocate(std::size_t bytes, std::size_t alignment) = 0;
107 
108     /*! Deallocates memory pointed to by \p p.
109      *
110      *  \param p pointer to be deallocated
111      *  \param bytes the size of the allocation. This must be equivalent to the value of \p bytes that
112      *      was passed to the allocation function that returned \p p.
113      *  \param alignment the size of the allocation. This must be equivalent to the value of \p alignment
114      *      that was passed to the allocation function that returned \p p.
115      */
116     virtual void do_deallocate(pointer p, std::size_t bytes, std::size_t alignment) = 0;
117 
118     /*! Compares this resource to the other one. The default implementation uses identity comparison,
119      *      which is often the right thing to do and doesn't require RTTI involvement.
120      *
121      *  \param other the other resource to compare this resource to
122      *  \returns whether the two resources are equivalent.
123      */
124     __host__ __device__
do_is_equal(const memory_resource & other)125     virtual bool do_is_equal(const memory_resource & other) const THRUST_NOEXCEPT
126     {
127         return this == &other;
128     }
129 };
130 
131 /*! The specialization of \p memory_resource for <tt>void *</tt>.
132  */
133 template<>
134 class memory_resource<void *>
135 #ifdef THRUST_STD_MR_NS
136     : THRUST_STD_MR_NS::memory_resource
137 #endif
138 {
139 public:
140     typedef void * pointer;
141 
~memory_resource()142     virtual ~memory_resource() THRUST_DEFAULT
143 
144     THRUST_NODISCARD
145     pointer allocate(std::size_t bytes, std::size_t alignment = THRUST_MR_DEFAULT_ALIGNMENT)
146     {
147         return do_allocate(bytes, alignment);
148     }
149 
150     void deallocate(pointer p, std::size_t bytes, std::size_t alignment = THRUST_MR_DEFAULT_ALIGNMENT)
151     {
152         do_deallocate(p, bytes, alignment);
153     }
154 
155     __host__ __device__
is_equal(const memory_resource & other)156     bool is_equal(const memory_resource & other) const THRUST_NOEXCEPT
157     {
158         return do_is_equal(other);
159     }
160 
161     virtual pointer do_allocate(std::size_t bytes, std::size_t alignment) = 0;
162     virtual void do_deallocate(pointer p, std::size_t bytes, std::size_t alignment) = 0;
163     __host__ __device__
do_is_equal(const memory_resource & other)164     virtual bool do_is_equal(const memory_resource & other) const THRUST_NOEXCEPT
165     {
166         return this == &other;
167     }
168 
169 #ifdef THRUST_STD_MR_NS
170     // the above do_is_equal is a different function than the one from the standard memory resource
171     // can't implement this reasonably without RTTI though; it's reasonable to assume false otherwise
172 
do_is_equal(const THRUST_STD_MR_NS::memory_resource & other)173     virtual bool do_is_equal(const THRUST_STD_MR_NS::memory_resource & other) const noexcept override
174     {
175 #  ifdef THRUST_HAS_DYNAMIC_CAST
176         auto mr_resource = dynamic_cast<memory_resource<> *>(&other);
177         return mr_resource && do_is_equal(*mr_resource);
178 #  else
179         return this == &other;
180 #  endif
181     }
182 #endif
183 };
184 
185 /*! Compares the memory resources for equality, first by identity, then by \p is_equal.
186  */
187 template<typename Pointer>
188 __host__ __device__
189 bool operator==(const memory_resource<Pointer> & lhs, const memory_resource<Pointer> & rhs) THRUST_NOEXCEPT
190 {
191     return &lhs == &rhs || rhs.is_equal(rhs);
192 }
193 
194 /*! Compares the memory resources for inequality, first by identity, then by \p is_equal.
195  */
196 template<typename Pointer>
197 __host__ __device__
198 bool operator!=(const memory_resource<Pointer> & lhs, const memory_resource<Pointer> & rhs) THRUST_NOEXCEPT
199 {
200     return !(lhs == rhs);
201 }
202 
203 /*! Returns a global instance of \p MR, created as a function local static variable.
204  *
205  *  \tparam MR type of a memory resource to get an instance from. Must be \p DefaultConstructible.
206  *  \returns a pointer to a global instance of \p MR.
207  */
208 template<typename MR>
209 __host__
get_global_resource()210 MR * get_global_resource()
211 {
212     static MR resource;
213     return &resource;
214 }
215 
216 /*! \}
217  */
218 
219 } // end mr
220 } // end thrust
221 
222