-
Notifications
You must be signed in to change notification settings - Fork 24
/
memory.cpp
275 lines (238 loc) · 8.28 KB
/
memory.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
// -*- mode: c++; tab-width: 4; indent-tabs-mode: t; eval: (progn (c-set-style "stroustrup") (c-set-offset 'innamespace 0)); -*-
// vi:set ts=4 sts=4 sw=4 noet :
//
// Copyright 2011, The TPIE development team
//
// This file is part of TPIE.
//
// TPIE is free software: you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// TPIE is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
// License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with TPIE. If not, see <http://www.gnu.org/licenses/>
#include "memory.h"
#include <iostream>
#include <sstream>
#include "tpie_log.h"
#include <cstring>
#include <cstdlib>
namespace tpie {
inline void segfault() {
std::abort();
}
memory_manager * mm = 0;
memory_manager::memory_manager(): m_used(0), m_limit(0), m_maxExceeded(0), m_nextWarning(0), m_enforce(ENFORCE_WARN) {}
size_t memory_manager::available() const throw() {
size_t used = m_used;
size_t limit = m_limit;
if (used < limit) return limit-used;
return 0;
}
void memory_manager::register_allocation(size_t bytes) {
boost::mutex::scoped_lock lock(m_mutex);
switch(m_enforce) {
case ENFORCE_IGNORE:
m_used += bytes;
break;
case ENFORCE_THROW:
if (m_used + bytes > m_limit && m_limit > 0) {
lock.unlock();
std::stringstream ss;
ss << "Memory allocation error, memory limit exceeded. "
<<"Allocation request \""
<< bytes
<< "\" plus previous allocation \""
<< m_used
<< "\" exceeds user-defined limit \""
<< m_limit
<< "\"";
throw out_of_memory_error(ss.str().c_str());
}
m_used += bytes;
break;
case ENFORCE_WARN:
m_used += bytes;
if (m_used > m_limit && m_used - m_limit > m_maxExceeded && m_limit > 0) {
m_maxExceeded = m_used - m_limit;
if (m_maxExceeded >= m_nextWarning) {
m_nextWarning = m_maxExceeded + m_maxExceeded/8;
lock.unlock();
log_warning() << "Memory limit exceeded by " << m_maxExceeded
<< " bytes, while trying to allocate " << bytes << " bytes."
<< " Limit is " << m_limit << ", but " << m_used << " would be used. " << std::endl;
}
}
};
}
void memory_manager::register_deallocation(size_t bytes) {
boost::mutex::scoped_lock lock(m_mutex);
#ifndef TPIE_NDEBUG
if (bytes > m_used) {
log_error() << "Error in deallocation, trying to deallocate " << bytes << " bytes, while only " <<
m_used << " were allocated" << std::endl;
segfault();
}
#endif
m_used -= bytes;
}
void memory_manager::set_limit(size_t new_limit) {
boost::mutex::scoped_lock lock(m_mutex);
m_limit = new_limit;
}
void memory_manager::set_enforcement(enforce_t e) {
boost::mutex::scoped_lock lock(m_mutex);
m_enforce = e;
}
///////////////////////////////////////////////////////////////////////////////
/// \internal \brief Buffers messages to the debug log.
/// TPIE logging might use the memory manager. We don't allow memory
/// allocation/deallocation while doing an allocation/deallocation, so messages
/// stored in the log_flusher aren't sent to the TPIE log until we are done
/// allocating/deallocating.
///////////////////////////////////////////////////////////////////////////////
struct log_flusher {
std::stringstream buf;
~log_flusher() {
std::string msg = buf.str();
if(!msg.empty()) {
tpie::log_debug() << msg;
tpie::log_debug().flush();
}
}
};
std::pair<uint8_t *, size_t> memory_manager::__allocate_consecutive(size_t upper_bound, size_t granularity) {
log_flusher lf;
boost::mutex::scoped_lock lock(m_mutex);
size_t high=available()/granularity;
if (upper_bound != 0)
high=std::min(upper_bound/granularity, high);
size_t low=1;
uint8_t * res;
//first check quickly if we can get "high" bytes of memory
//directly.
try {
res = new uint8_t[high*granularity];
m_used += high*granularity;
#ifndef TPIE_NDEBUG
__register_pointer(res, high*granularity, typeid(uint8_t) );
#endif
return std::make_pair(res, high*granularity);
} catch (std::bad_alloc) {
lf.buf << "Failed to get " << (high*granularity)/(1024*1024) << " megabytes of memory. "
<< "Performing binary search to find largest amount "
<< "of memory available. This might take a few moments.\n";
}
//perform a binary search in [low,high] for highest possible
//memory allocation
size_t best=0;
do {
size_t mid = static_cast<size_t>((static_cast<uint64_t>(low)+high)/2);
lf.buf << "Search area is [" << low*granularity << "," << high*granularity << "]"
<< " query amount is: " << mid*granularity << ":\n";
//try to allocate "mid" bytes of memory
//std::new throws an exception if memory allocation fails
try {
uint8_t* mem = new uint8_t[mid*granularity];
low = mid+1;
best=mid*granularity;
delete[] mem;
} catch (std::bad_alloc) {
high = mid-1;
lf.buf << " failed.\n";
}
} while (high >= low);
lf.buf << "- - - - - - - END MEMORY SEARCH - - - - - -\n";
res = new uint8_t[best];
m_used += best;
#ifndef TPIE_NDEBUG
__register_pointer(res, best, typeid(uint8_t) );
#endif
return std::make_pair(res, best);
}
#ifndef TPIE_NDEBUG
void memory_manager::register_pointer(void * p, size_t size, const std::type_info & t) {
boost::mutex::scoped_lock lock(m_mutex);
__register_pointer(p, size, t);
}
void memory_manager::__register_pointer(void * p, size_t size, const std::type_info & t) {
if (m_pointers.count(p) != 0) {
log_error() << "Trying to register pointer " << p << " of size "
<< size << " which is already registered" << std::endl;
segfault();
}
m_pointers[p] = std::make_pair(size, &t);;
}
void memory_manager::unregister_pointer(void * p, size_t size, const std::type_info & t) {
boost::mutex::scoped_lock lock(m_mutex);
__unregister_pointer(p, size, t);
}
void memory_manager::__unregister_pointer(void * p, size_t size, const std::type_info & t) {
boost::unordered_map<void *, std::pair<size_t, const std::type_info *> >::const_iterator i=m_pointers.find(p);
if (i == m_pointers.end()) {
log_error() << "Trying to deregister pointer " << p << " of size "
<< size << " which was never registered" << std::endl;
segfault();
} else {
if (i->second.first != size) {
log_error() << "Trying to deregister pointer " << p << " of size "
<< size << " which was registered with size " << i->second.first << std::endl;
segfault();
}
if (*i->second.second != t) {
log_error() << "Trying to deregister pointer " << p << " of type "
<< t.name() << " which was registered with size " << i->second.second->name() << std::endl;
segfault();
}
m_pointers.erase(i);
}
}
void memory_manager::assert_tpie_ptr(void * p) {
boost::mutex::scoped_lock lock(m_mutex);
__assert_tpie_ptr(p);
}
void memory_manager::__assert_tpie_ptr(void * p) {
if (!p || m_pointers.count(p)) return;
log_error() << p << " has not been allocated with tpie_new" << std::endl;
segfault();
}
void memory_manager::complain_about_unfreed_memory() {
boost::mutex::scoped_lock lock(m_mutex);
__complain_about_unfreed_memory();
}
void memory_manager::__complain_about_unfreed_memory() {
if(m_pointers.size() == 0) return;
log_error() << "The following pointers were either leaked or deleted with delete instead of tpie_delete" << std::endl << std::endl;
for(boost::unordered_map<void *, std::pair<size_t, const std::type_info *> >::const_iterator i=m_pointers.begin();
i != m_pointers.end(); ++i)
log_error() << " " << i->first << ": " << i->second.second->name() << " of " << i->second.first << " bytes" << std::endl;
}
#endif
void init_memory_manager() {
mm = new memory_manager();
}
void finish_memory_manager() {
#ifndef TPIE_NDEBUG
mm->complain_about_unfreed_memory();
#endif
delete mm;
mm = 0;
}
memory_manager & get_memory_manager() {
#ifndef TPIE_NDEBUG
if (mm == 0) throw std::runtime_error("Memory management not initialized");
#endif
return * mm;
}
size_t consecutive_memory_available(size_t granularity) {
std::pair<uint8_t *, size_t> r = get_memory_manager().__allocate_consecutive(0, granularity);
tpie_delete_array(r.first, r.second);
return r.second;
}
} //namespace tpieg