1 /* 2 Copyright (c) 2005-2021 Intel 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 #ifndef __TBB_memory_pool_H 18 #define __TBB_memory_pool_H 19 20 #if !TBB_PREVIEW_MEMORY_POOL 21 #error Set TBB_PREVIEW_MEMORY_POOL to include memory_pool.h 22 #endif 23 /** @file */ 24 25 #include "scalable_allocator.h" 26 27 #include <new> // std::bad_alloc 28 #include <stdexcept> // std::runtime_error, std::invalid_argument 29 #include <utility> // std::forward 30 31 32 #if __TBB_EXTRA_DEBUG 33 #define __TBBMALLOC_ASSERT ASSERT 34 #else 35 #define __TBBMALLOC_ASSERT(a,b) ((void)0) 36 #endif 37 38 namespace tbb { 39 namespace detail { 40 namespace d1 { 41 42 //! Base of thread-safe pool allocator for variable-size requests 43 class pool_base : no_copy { 44 // Pool interface is separate from standard allocator classes because it has 45 // to maintain internal state, no copy or assignment. Move and swap are possible. 46 public: 47 //! Reset pool to reuse its memory (free all objects at once) 48 void recycle() { rml::pool_reset(my_pool); } 49 50 //! The "malloc" analogue to allocate block of memory of size bytes 51 void *malloc(size_t size) { return rml::pool_malloc(my_pool, size); } 52 53 //! The "free" analogue to discard a previously allocated piece of memory. 54 void free(void* ptr) { rml::pool_free(my_pool, ptr); } 55 56 //! The "realloc" analogue complementing pool_malloc. 57 // Enables some low-level optimization possibilities 58 void *realloc(void* ptr, size_t size) { 59 return rml::pool_realloc(my_pool, ptr, size); 60 } 61 62 protected: 63 //! destroy pool - must be called in a child class 64 void destroy() { rml::pool_destroy(my_pool); } 65 66 rml::MemoryPool *my_pool; 67 }; 68 69 #if _MSC_VER && !defined(__INTEL_COMPILER) 70 // Workaround for erroneous "unreferenced parameter" warning in method destroy. 71 #pragma warning (push) 72 #pragma warning (disable: 4100) 73 #endif 74 75 //! Meets "allocator" requirements of ISO C++ Standard, Section 20.1.5 76 /** @ingroup memory_allocation */ 77 template<typename T, typename P = pool_base> 78 class memory_pool_allocator { 79 protected: 80 typedef P pool_type; 81 pool_type *my_pool; 82 template<typename U, typename R> 83 friend class memory_pool_allocator; 84 template<typename V, typename U, typename R> 85 friend bool operator==( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b); 86 template<typename V, typename U, typename R> 87 friend bool operator!=( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b); 88 public: 89 typedef T value_type; 90 typedef value_type* pointer; 91 typedef const value_type* const_pointer; 92 typedef value_type& reference; 93 typedef const value_type& const_reference; 94 typedef size_t size_type; 95 typedef ptrdiff_t difference_type; 96 template<typename U> struct rebind { 97 typedef memory_pool_allocator<U, P> other; 98 }; 99 100 explicit memory_pool_allocator(pool_type &pool) throw() : my_pool(&pool) {} 101 memory_pool_allocator(const memory_pool_allocator& src) throw() : my_pool(src.my_pool) {} 102 template<typename U> 103 memory_pool_allocator(const memory_pool_allocator<U,P>& src) throw() : my_pool(src.my_pool) {} 104 105 pointer address(reference x) const { return &x; } 106 const_pointer address(const_reference x) const { return &x; } 107 108 //! Allocate space for n objects. 109 pointer allocate( size_type n, const void* /*hint*/ = 0) { 110 pointer p = static_cast<pointer>( my_pool->malloc( n*sizeof(value_type) ) ); 111 if (!p) 112 throw_exception(std::bad_alloc()); 113 return p; 114 } 115 //! Free previously allocated block of memory. 116 void deallocate( pointer p, size_type ) { 117 my_pool->free(p); 118 } 119 //! Largest value for which method allocate might succeed. 120 size_type max_size() const throw() { 121 size_type max = static_cast<size_type>(-1) / sizeof (value_type); 122 return (max > 0 ? max : 1); 123 } 124 //! Copy-construct value at location pointed to by p. 125 126 template<typename U, typename... Args> 127 void construct(U *p, Args&&... args) 128 { ::new((void *)p) U(std::forward<Args>(args)...); } 129 130 //! Destroy value at location pointed to by p. 131 void destroy( pointer p ) { p->~value_type(); } 132 133 }; 134 135 #if _MSC_VER && !defined(__INTEL_COMPILER) 136 #pragma warning (pop) 137 #endif // warning 4100 is back 138 139 //! Analogous to std::allocator<void>, as defined in ISO C++ Standard, Section 20.4.1 140 /** @ingroup memory_allocation */ 141 template<typename P> 142 class memory_pool_allocator<void, P> { 143 public: 144 typedef P pool_type; 145 typedef void* pointer; 146 typedef const void* const_pointer; 147 typedef void value_type; 148 template<typename U> struct rebind { 149 typedef memory_pool_allocator<U, P> other; 150 }; 151 152 explicit memory_pool_allocator( pool_type &pool) throw() : my_pool(&pool) {} 153 memory_pool_allocator( const memory_pool_allocator& src) throw() : my_pool(src.my_pool) {} 154 template<typename U> 155 memory_pool_allocator(const memory_pool_allocator<U,P>& src) throw() : my_pool(src.my_pool) {} 156 157 protected: 158 pool_type *my_pool; 159 template<typename U, typename R> 160 friend class memory_pool_allocator; 161 template<typename V, typename U, typename R> 162 friend bool operator==( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b); 163 template<typename V, typename U, typename R> 164 friend bool operator!=( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b); 165 }; 166 167 template<typename T, typename U, typename P> 168 inline bool operator==( const memory_pool_allocator<T,P>& a, const memory_pool_allocator<U,P>& b) {return a.my_pool==b.my_pool;} 169 170 template<typename T, typename U, typename P> 171 inline bool operator!=( const memory_pool_allocator<T,P>& a, const memory_pool_allocator<U,P>& b) {return a.my_pool!=b.my_pool;} 172 173 //! Thread-safe growable pool allocator for variable-size requests 174 template <typename Alloc> 175 class memory_pool : public pool_base { 176 Alloc my_alloc; // TODO: base-class optimization 177 static void *allocate_request(intptr_t pool_id, size_t & bytes); 178 static int deallocate_request(intptr_t pool_id, void*, size_t raw_bytes); 179 180 public: 181 //! construct pool with underlying allocator 182 explicit memory_pool(const Alloc &src = Alloc()); 183 184 //! destroy pool 185 ~memory_pool() { destroy(); } // call the callbacks first and destroy my_alloc latter 186 }; 187 188 class fixed_pool : public pool_base { 189 void *my_buffer; 190 size_t my_size; 191 inline static void *allocate_request(intptr_t pool_id, size_t & bytes); 192 193 public: 194 //! construct pool with underlying allocator 195 inline fixed_pool(void *buf, size_t size); 196 //! destroy pool 197 ~fixed_pool() { destroy(); } 198 }; 199 200 //////////////// Implementation /////////////// 201 202 template <typename Alloc> 203 memory_pool<Alloc>::memory_pool(const Alloc &src) : my_alloc(src) { 204 rml::MemPoolPolicy args(allocate_request, deallocate_request, 205 sizeof(typename Alloc::value_type)); 206 rml::MemPoolError res = rml::pool_create_v1(intptr_t(this), &args, &my_pool); 207 if (res!=rml::POOL_OK) 208 throw_exception(std::runtime_error("Can't create pool")); 209 } 210 template <typename Alloc> 211 void *memory_pool<Alloc>::allocate_request(intptr_t pool_id, size_t & bytes) { 212 memory_pool<Alloc> &self = *reinterpret_cast<memory_pool<Alloc>*>(pool_id); 213 const size_t unit_size = sizeof(typename Alloc::value_type); 214 __TBBMALLOC_ASSERT( 0 == bytes%unit_size, NULL); 215 void *ptr; 216 #if TBB_USE_EXCEPTIONS 217 try { 218 #endif 219 ptr = self.my_alloc.allocate( bytes/unit_size ); 220 #if TBB_USE_EXCEPTIONS 221 } catch(...) { 222 return 0; 223 } 224 #endif 225 return ptr; 226 } 227 #if __TBB_MSVC_UNREACHABLE_CODE_IGNORED 228 // Workaround for erroneous "unreachable code" warning in the template below. 229 // Specific for VC++ 17-18 compiler 230 #pragma warning (push) 231 #pragma warning (disable: 4702) 232 #endif 233 template <typename Alloc> 234 int memory_pool<Alloc>::deallocate_request(intptr_t pool_id, void* raw_ptr, size_t raw_bytes) { 235 memory_pool<Alloc> &self = *reinterpret_cast<memory_pool<Alloc>*>(pool_id); 236 const size_t unit_size = sizeof(typename Alloc::value_type); 237 __TBBMALLOC_ASSERT( 0 == raw_bytes%unit_size, NULL); 238 self.my_alloc.deallocate( static_cast<typename Alloc::value_type*>(raw_ptr), raw_bytes/unit_size ); 239 return 0; 240 } 241 #if __TBB_MSVC_UNREACHABLE_CODE_IGNORED 242 #pragma warning (pop) 243 #endif 244 inline fixed_pool::fixed_pool(void *buf, size_t size) : my_buffer(buf), my_size(size) { 245 if (!buf || !size) 246 // TODO: improve support for mode with exceptions disabled 247 throw_exception(std::invalid_argument("Zero in parameter is invalid")); 248 rml::MemPoolPolicy args(allocate_request, 0, size, /*fixedPool=*/true); 249 rml::MemPoolError res = rml::pool_create_v1(intptr_t(this), &args, &my_pool); 250 if (res!=rml::POOL_OK) 251 throw_exception(std::runtime_error("Can't create pool")); 252 } 253 inline void *fixed_pool::allocate_request(intptr_t pool_id, size_t & bytes) { 254 fixed_pool &self = *reinterpret_cast<fixed_pool*>(pool_id); 255 __TBBMALLOC_ASSERT(0 != self.my_size, "The buffer must not be used twice."); 256 bytes = self.my_size; 257 self.my_size = 0; // remember that buffer has been used 258 return self.my_buffer; 259 } 260 261 } // namespace d1 262 } // namespace detail 263 264 inline namespace v1 { 265 using detail::d1::memory_pool_allocator; 266 using detail::d1::memory_pool; 267 using detail::d1::fixed_pool; 268 } // inline namepspace v1 269 } // namespace tbb 270 271 #undef __TBBMALLOC_ASSERT 272 #endif// __TBB_memory_pool_H 273