1 | /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
2 | // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4: |
3 | #ident "$Id$" |
4 | /*====== |
5 | This file is part of PerconaFT. |
6 | |
7 | |
8 | Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved. |
9 | |
10 | PerconaFT is free software: you can redistribute it and/or modify |
11 | it under the terms of the GNU General Public License, version 2, |
12 | as published by the Free Software Foundation. |
13 | |
14 | PerconaFT is distributed in the hope that it will be useful, |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | GNU General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU General Public License |
20 | along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. |
21 | |
22 | ---------------------------------------- |
23 | |
24 | PerconaFT is free software: you can redistribute it and/or modify |
25 | it under the terms of the GNU Affero General Public License, version 3, |
26 | as published by the Free Software Foundation. |
27 | |
28 | PerconaFT is distributed in the hope that it will be useful, |
29 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
30 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
31 | GNU Affero General Public License for more details. |
32 | |
33 | You should have received a copy of the GNU Affero General Public License |
34 | along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. |
35 | ======= */ |
36 | |
37 | #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved." |
38 | |
39 | #include <algorithm> |
40 | #include <string.h> |
41 | #include <memory.h> |
42 | |
43 | #include <util/memarena.h> |
44 | |
45 | void memarena::create(size_t initial_size) { |
46 | _current_chunk = arena_chunk(); |
47 | _other_chunks = nullptr; |
48 | _size_of_other_chunks = 0; |
49 | _footprint_of_other_chunks = 0; |
50 | _n_other_chunks = 0; |
51 | |
52 | _current_chunk.size = initial_size; |
53 | if (_current_chunk.size > 0) { |
54 | XMALLOC_N(_current_chunk.size, _current_chunk.buf); |
55 | } |
56 | } |
57 | |
58 | void memarena::destroy(void) { |
59 | if (_current_chunk.buf) { |
60 | toku_free(_current_chunk.buf); |
61 | } |
62 | for (int i = 0; i < _n_other_chunks; i++) { |
63 | toku_free(_other_chunks[i].buf); |
64 | } |
65 | if (_other_chunks) { |
66 | toku_free(_other_chunks); |
67 | } |
68 | _current_chunk = arena_chunk(); |
69 | _other_chunks = nullptr; |
70 | _n_other_chunks = 0; |
71 | } |
72 | |
73 | static size_t round_to_page(size_t size) { |
74 | const size_t page_size = 4096; |
75 | const size_t r = page_size + ((size - 1) & ~(page_size - 1)); |
76 | assert((r & (page_size - 1)) == 0); // make sure it's aligned |
77 | assert(r >= size); // make sure it's not too small |
78 | assert(r < size + page_size); // make sure we didn't grow by more than a page. |
79 | return r; |
80 | } |
81 | |
82 | static const size_t MEMARENA_MAX_CHUNK_SIZE = 64 * 1024 * 1024; |
83 | |
84 | void *memarena::malloc_from_arena(size_t size) { |
85 | if (_current_chunk.buf == nullptr || _current_chunk.size < _current_chunk.used + size) { |
86 | // The existing block isn't big enough. |
87 | // Add the block to the vector of blocks. |
88 | if (_current_chunk.buf) { |
89 | invariant(_current_chunk.size > 0); |
90 | int old_n = _n_other_chunks; |
91 | XREALLOC_N(old_n + 1, _other_chunks); |
92 | _other_chunks[old_n] = _current_chunk; |
93 | _n_other_chunks = old_n + 1; |
94 | _size_of_other_chunks += _current_chunk.size; |
95 | _footprint_of_other_chunks += toku_memory_footprint(_current_chunk.buf, _current_chunk.used); |
96 | } |
97 | |
98 | // Make a new one. Grow the buffer size exponentially until we hit |
99 | // the max chunk size, but make it at least `size' bytes so the |
100 | // current allocation always fit. |
101 | size_t new_size = std::min(MEMARENA_MAX_CHUNK_SIZE, 2 * _current_chunk.size); |
102 | if (new_size < size) { |
103 | new_size = size; |
104 | } |
105 | new_size = round_to_page(new_size); // at least size, but round to the next page size |
106 | XMALLOC_N(new_size, _current_chunk.buf); |
107 | _current_chunk.used = 0; |
108 | _current_chunk.size = new_size; |
109 | } |
110 | invariant(_current_chunk.buf != nullptr); |
111 | |
112 | // allocate in the existing block. |
113 | char *p = _current_chunk.buf + _current_chunk.used; |
114 | _current_chunk.used += size; |
115 | return p; |
116 | } |
117 | |
118 | void memarena::move_memory(memarena *dest) { |
119 | // Move memory to dest |
120 | XREALLOC_N(dest->_n_other_chunks + _n_other_chunks + 1, dest->_other_chunks); |
121 | dest->_size_of_other_chunks += _size_of_other_chunks + _current_chunk.size; |
122 | dest->_footprint_of_other_chunks += _footprint_of_other_chunks + toku_memory_footprint(_current_chunk.buf, _current_chunk.used); |
123 | for (int i = 0; i < _n_other_chunks; i++) { |
124 | dest->_other_chunks[dest->_n_other_chunks++] = _other_chunks[i]; |
125 | } |
126 | dest->_other_chunks[dest->_n_other_chunks++] = _current_chunk; |
127 | |
128 | // Clear out this memarena's memory |
129 | toku_free(_other_chunks); |
130 | _current_chunk = arena_chunk(); |
131 | _other_chunks = nullptr; |
132 | _size_of_other_chunks = 0; |
133 | _footprint_of_other_chunks = 0; |
134 | _n_other_chunks = 0; |
135 | } |
136 | |
137 | size_t memarena::total_memory_size(void) const { |
138 | return sizeof(*this) + |
139 | total_size_in_use() + |
140 | _n_other_chunks * sizeof(*_other_chunks); |
141 | } |
142 | |
143 | size_t memarena::total_size_in_use(void) const { |
144 | return _size_of_other_chunks + _current_chunk.used; |
145 | } |
146 | |
147 | size_t memarena::(void) const { |
148 | return sizeof(*this) + |
149 | _footprint_of_other_chunks + |
150 | toku_memory_footprint(_current_chunk.buf, _current_chunk.used) + |
151 | _n_other_chunks * sizeof(*_other_chunks); |
152 | } |
153 | |
154 | //////////////////////////////////////////////////////////////////////////////// |
155 | |
156 | const void *memarena::chunk_iterator::current(size_t *used) const { |
157 | if (_chunk_idx < 0) { |
158 | *used = _ma->_current_chunk.used; |
159 | return _ma->_current_chunk.buf; |
160 | } else if (_chunk_idx < _ma->_n_other_chunks) { |
161 | *used = _ma->_other_chunks[_chunk_idx].used; |
162 | return _ma->_other_chunks[_chunk_idx].buf; |
163 | } |
164 | *used = 0; |
165 | return nullptr; |
166 | } |
167 | |
168 | void memarena::chunk_iterator::next() { |
169 | _chunk_idx++; |
170 | } |
171 | |
172 | bool memarena::chunk_iterator::more() const { |
173 | if (_chunk_idx < 0) { |
174 | return _ma->_current_chunk.buf != nullptr; |
175 | } |
176 | return _chunk_idx < _ma->_n_other_chunks; |
177 | } |
178 | |