/* * Copyright (C) 2018 - 2020 Intel Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice(s), * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice(s), * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include #include #include #include #include #include #include #include "memkind.h" /* * Header file for the C++ allocator compatible with the C++ standard library allocator concepts. * More details in pmemallocator(3) man page. * Note: memory heap management is based on memkind_malloc, refer to the memkind(3) man page for more * information. * * Functionality defined in this header is considered as stable API (STANDARD API). * API standards are described in memkind(3) man page. */ namespace libmemkind { enum class allocation_policy { DEFAULT = MEMKIND_MEM_USAGE_POLICY_DEFAULT, CONSERVATIVE = MEMKIND_MEM_USAGE_POLICY_CONSERVATIVE, MAX }; namespace pmem { namespace internal { class kind_wrapper_t { public: kind_wrapper_t(const char *dir, std::size_t max_size, libmemkind::allocation_policy alloc_policy= libmemkind::allocation_policy::DEFAULT) { cfg = memkind_config_new(); if (!cfg) { throw std::runtime_error( std::string("An error occurred while creating pmem config")); } memkind_config_set_path(cfg, dir); memkind_config_set_size(cfg, max_size); memkind_config_set_memory_usage_policy(cfg, static_cast(alloc_policy)); int err_c = memkind_create_pmem_with_config(cfg, &kind); memkind_config_delete(cfg); if (err_c) { throw std::runtime_error( std::string("An error occurred while creating pmem kind; error code: ") + std::to_string(err_c)); } } kind_wrapper_t(const kind_wrapper_t &) = delete; void operator=(const kind_wrapper_t &) = delete; ~kind_wrapper_t() { memkind_destroy_kind(kind); } memkind_t get() const { return kind; } private: memkind_t kind; struct memkind_config *cfg; }; } template class allocator { using kind_wrapper_t = internal::kind_wrapper_t; std::shared_ptr kind_wrapper_ptr; public: using value_type = T; using pointer = value_type*; using const_pointer = const value_type*; using reference = value_type&; using const_reference = const value_type&; using size_type = size_t; using difference_type = ptrdiff_t; template struct rebind { using other = allocator; }; template friend class allocator; #if !_GLIBCXX_USE_CXX11_ABI /* This is a workaround for compilers (e.g GCC 4.8) that uses C++11 standard, * but use old - non C++11 ABI */ template explicit allocator() { static_assert(std::is_same::value, "libmemkind::pmem::allocator cannot be compiled without CXX11 ABI"); } #endif explicit allocator(const char *dir, size_t max_size) : kind_wrapper_ptr(std::make_shared(dir, max_size)) { } explicit allocator(const std::string &dir, size_t max_size) : allocator(dir.c_str(), max_size) { } explicit allocator(const char *dir, size_t max_size, libmemkind::allocation_policy alloc_policy) : kind_wrapper_ptr(std::make_shared(dir, max_size, alloc_policy)) { } explicit allocator(const std::string &dir, size_t max_size, libmemkind::allocation_policy alloc_policy) : allocator(dir.c_str(), max_size, alloc_policy) { } allocator(const allocator &other) = default; template allocator(const allocator &other) noexcept : kind_wrapper_ptr( other.kind_wrapper_ptr) { } allocator(allocator &&other) = default; template allocator(allocator &&other) noexcept : kind_wrapper_ptr(std::move(other.kind_wrapper_ptr)) { } allocator &operator = (const allocator &other) = default; template allocator &operator = (const allocator &other) noexcept { kind_wrapper_ptr = other.kind_wrapper_ptr; return *this; } allocator &operator = (allocator &&other) = default; template allocator &operator = (allocator &&other) noexcept { kind_wrapper_ptr = std::move(other.kind_wrapper_ptr); return *this; } pointer allocate(size_type n) const { pointer result = static_cast(memkind_malloc(kind_wrapper_ptr->get(), n*sizeof(T))); if (!result) { throw std::bad_alloc(); } return result; } void deallocate(pointer p, size_type n) const { memkind_free(kind_wrapper_ptr->get(), static_cast(p)); } template void construct(U *p, Args &&... args) const { ::new((void *)p) U(std::forward(args)...); } void destroy(pointer p) const { p->~value_type(); } template friend bool operator ==(const allocator &lhs, const allocator &rhs); template friend bool operator !=(const allocator &lhs, const allocator &rhs); }; template bool operator ==(const allocator &lhs, const allocator &rhs) { return lhs.kind_wrapper_ptr->get() == rhs.kind_wrapper_ptr->get(); } template bool operator !=(const allocator &lhs, const allocator &rhs) { return !(lhs == rhs); } } // namespace pmem } // namespace libmemkind