1 /*******************************************************************************
2  * thrill/mem/allocator_base.hpp
3  *
4  * Part of Project Thrill - http://project-thrill.org
5  *
6  * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
7  *
8  * All rights reserved. Published under the BSD-2 license in the LICENSE file.
9  ******************************************************************************/
10 
11 #pragma once
12 #ifndef THRILL_MEM_ALLOCATOR_BASE_HEADER
13 #define THRILL_MEM_ALLOCATOR_BASE_HEADER
14 
15 #include <thrill/common/string.hpp>
16 #include <thrill/mem/malloc_tracker.hpp>
17 #include <thrill/mem/manager.hpp>
18 #include <tlx/allocator_base.hpp>
19 #include <tlx/string/ssprintf_generic.hpp>
20 
21 #include <atomic>
22 #include <cassert>
23 #include <deque>
24 #include <iosfwd>
25 #include <new>
26 #include <string>
27 #include <type_traits>
28 #include <vector>
29 
30 namespace thrill {
31 namespace mem {
32 
33 /******************************************************************************/
34 // FixedAllocator
35 
36 template <typename Type, Manager& manager_>
37 class FixedAllocator : public tlx::AllocatorBase<Type>
38 {
39     static constexpr bool debug = false;
40 
41 public:
42     using value_type = Type;
43     using pointer = Type *;
44     using const_pointer = const Type *;
45     using reference = Type&;
46     using const_reference = const Type&;
47     using size_type = std::size_t;
48     using difference_type = std::ptrdiff_t;
49 
50     //! C++11 type flag
51     using is_always_equal = std::true_type;
52 
53     //! Return allocator for different type.
54     template <typename U>
55     struct rebind { using other = FixedAllocator<U, manager_>; };
56 
57     //! default constructor
58     FixedAllocator() noexcept = default;
59 
60     //! copy-constructor
61     FixedAllocator(const FixedAllocator&) noexcept = default;
62 
63     //! copy-constructor from a rebound allocator
64     template <typename OtherType>
FixedAllocator(const FixedAllocator<OtherType,manager_> &)65     FixedAllocator(const FixedAllocator<OtherType, manager_>&) noexcept
66     { }
67 
68 #if !defined(_MSC_VER)
69     //! copy-assignment operator: default
70     FixedAllocator& operator = (FixedAllocator&) noexcept = default;
71 
72     //! move-assignment operator: default
73     FixedAllocator& operator = (FixedAllocator&&) noexcept = default;
74 #endif
75 
76     //! Attempts to allocate a block of storage with a size large enough to
77     //! contain n elements of member type value_type, and returns a pointer to
78     //! the first element.
allocate(size_type n,const void * =nullptr)79     pointer allocate(size_type n, const void* /* hint */ = nullptr) {
80         if (n > this->max_size())
81             throw std::bad_alloc();
82 
83         const size_t size = n * sizeof(Type);
84         manager_.add(size);
85 
86         if (debug) {
87             printf("allocate() n=%zu sizeof(T)=%zu total=%zu\n",
88                    n, sizeof(Type), manager_.total());
89         }
90 
91         Type* r = static_cast<Type*>(bypass_malloc(size));
92         while (r == nullptr)
93         {
94             // If malloc fails and there is a std::new_handler, call it to try
95             // free up memory.
96             std::new_handler nh = std::get_new_handler();
97             if (!nh)
98                 throw std::bad_alloc();
99             nh();
100             r = static_cast<Type*>(bypass_malloc(size));
101         }
102         return r;
103     }
104 
105     //! Releases a block of storage previously allocated with member allocate
106     //! and not yet released.
deallocate(pointer p,size_type n) const107     void deallocate(pointer p, size_type n) const noexcept {
108 
109         manager_.subtract(n * sizeof(Type));
110 
111         if (debug) {
112             printf("deallocate() n=%zu sizeof(T)=%zu total=%zu\n",
113                    n, sizeof(Type), manager_.total());
114         }
115 
116         bypass_free(p, n * sizeof(Type));
117     }
118 
119     //! Compare to another allocator of same type
120     template <typename Other>
operator ==(const FixedAllocator<Other,manager_> &) const121     bool operator == (const FixedAllocator<Other, manager_>&) const noexcept {
122         return true;
123     }
124 
125     //! Compare to another allocator of same type
126     template <typename Other>
operator !=(const FixedAllocator<Other,manager_> &) const127     bool operator != (const FixedAllocator<Other, manager_>&) const noexcept {
128         return true;
129     }
130 };
131 
132 template <Manager& manager_>
133 class FixedAllocator<void, manager_>
134 {
135 public:
136     using pointer = void*;
137     using const_pointer = const void*;
138     using value_type = void;
139 
140     template <typename U>
141     struct rebind { using other = FixedAllocator<U, manager_>; };
142 };
143 
144 /******************************************************************************/
145 // BypassAllocator
146 
147 //! global bypass memory manager
148 extern Manager g_bypass_manager;
149 
150 //! instantiate FixedAllocator as BypassAllocator
151 template <typename Type>
152 using BypassAllocator = FixedAllocator<Type, g_bypass_manager>;
153 
154 //! operator new with our Allocator
155 template <typename T, typename... Args>
156 T * by_new(Args&& ... args) {
157     BypassAllocator<T> allocator;
158     T* value = allocator.allocate(1);
159     allocator.construct(value, std::forward<Args>(args) ...);
160     return value;
161 }
162 
163 //! operator delete with our Allocator
164 template <typename T>
by_delete(T * value)165 void by_delete(T* value) {
166     BypassAllocator<T> allocator;
167     allocator.destroy(value);
168     allocator.deallocate(value, 1);
169 }
170 
171 /******************************************************************************/
172 // template aliases with BypassAllocator
173 
174 //! string without malloc tracking
175 using by_string = std::basic_string<
176     char, std::char_traits<char>, BypassAllocator<char> >;
177 
178 //! stringbuf without malloc tracking
179 using by_stringbuf = std::basic_stringbuf<
180     char, std::char_traits<char>, BypassAllocator<char> >;
181 
182 //! vector without malloc tracking
183 template <typename T>
184 using by_vector = std::vector<T, BypassAllocator<T> >;
185 
186 //! deque without malloc tracking
187 template <typename T>
188 using by_deque = std::deque<T, BypassAllocator<T> >;
189 
190 //! convert to string
to_string(int val)191 static inline by_string to_string(int val) {
192     return tlx::ssnprintf_generic<by_string>(4 * sizeof(int), "%d", val);
193 }
194 
195 //! convert to string
to_string(unsigned val)196 static inline by_string to_string(unsigned val) {
197     return tlx::ssnprintf_generic<by_string>(4 * sizeof(int), "%u", val);
198 }
199 
200 //! convert to string
to_string(long val)201 static inline by_string to_string(long val) {
202     return tlx::ssnprintf_generic<by_string>(4 * sizeof(long), "%ld", val);
203 }
204 
205 //! convert to string
to_string(unsigned long val)206 static inline by_string to_string(unsigned long val) {
207     return tlx::ssnprintf_generic<by_string>(4 * sizeof(long), "%lu", val);
208 }
209 
210 //! convert to string
to_string(long long val)211 static inline by_string to_string(long long val) {
212     return tlx::ssnprintf_generic<by_string>(4 * sizeof(long long), "%lld", val);
213 }
214 
215 //! convert to string
to_string(unsigned long long val)216 static inline by_string to_string(unsigned long long val) {
217     return tlx::ssnprintf_generic<by_string>(4 * sizeof(long long), "%llu", val);
218 }
219 
220 } // namespace mem
221 } // namespace thrill
222 
223 #endif // !THRILL_MEM_ALLOCATOR_BASE_HEADER
224 
225 /******************************************************************************/
226