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
38 Licensed under the Apache License, Version 2.0 (the "License");
39 you may not use this file except in compliance with the License.
40 You may obtain a copy of the License at
41
42 http://www.apache.org/licenses/LICENSE-2.0
43
44 Unless required by applicable law or agreed to in writing, software
45 distributed under the License is distributed on an "AS IS" BASIS,
46 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
47 See the License for the specific language governing permissions and
48 limitations under the License.
49 ======= */
50
51 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
52
53 #include <algorithm>
54 #include <string.h>
55 #include <memory.h>
56
57 #include <util/memarena.h>
58
create(size_t initial_size)59 void memarena::create(size_t initial_size) {
60 _current_chunk = arena_chunk();
61 _other_chunks = nullptr;
62 _size_of_other_chunks = 0;
63 _footprint_of_other_chunks = 0;
64 _n_other_chunks = 0;
65
66 _current_chunk.size = initial_size;
67 if (_current_chunk.size > 0) {
68 XMALLOC_N(_current_chunk.size, _current_chunk.buf);
69 }
70 }
71
destroy(void)72 void memarena::destroy(void) {
73 if (_current_chunk.buf) {
74 toku_free(_current_chunk.buf);
75 }
76 for (int i = 0; i < _n_other_chunks; i++) {
77 toku_free(_other_chunks[i].buf);
78 }
79 if (_other_chunks) {
80 toku_free(_other_chunks);
81 }
82 _current_chunk = arena_chunk();
83 _other_chunks = nullptr;
84 _n_other_chunks = 0;
85 }
86
round_to_page(size_t size)87 static size_t round_to_page(size_t size) {
88 const size_t page_size = 4096;
89 const size_t r = page_size + ((size - 1) & ~(page_size - 1));
90 assert((r & (page_size - 1)) == 0); // make sure it's aligned
91 assert(r >= size); // make sure it's not too small
92 assert(r < size + page_size); // make sure we didn't grow by more than a page.
93 return r;
94 }
95
96 static const size_t MEMARENA_MAX_CHUNK_SIZE = 64 * 1024 * 1024;
97
malloc_from_arena(size_t size)98 void *memarena::malloc_from_arena(size_t size) {
99 if (_current_chunk.buf == nullptr || _current_chunk.size < _current_chunk.used + size) {
100 // The existing block isn't big enough.
101 // Add the block to the vector of blocks.
102 if (_current_chunk.buf) {
103 invariant(_current_chunk.size > 0);
104 int old_n = _n_other_chunks;
105 XREALLOC_N(old_n + 1, _other_chunks);
106 _other_chunks[old_n] = _current_chunk;
107 _n_other_chunks = old_n + 1;
108 _size_of_other_chunks += _current_chunk.size;
109 _footprint_of_other_chunks += toku_memory_footprint(_current_chunk.buf, _current_chunk.used);
110 }
111
112 // Make a new one. Grow the buffer size exponentially until we hit
113 // the max chunk size, but make it at least `size' bytes so the
114 // current allocation always fit.
115 size_t new_size = std::min(MEMARENA_MAX_CHUNK_SIZE, 2 * _current_chunk.size);
116 if (new_size < size) {
117 new_size = size;
118 }
119 new_size = round_to_page(new_size); // at least size, but round to the next page size
120 XMALLOC_N(new_size, _current_chunk.buf);
121 _current_chunk.used = 0;
122 _current_chunk.size = new_size;
123 }
124 invariant(_current_chunk.buf != nullptr);
125
126 // allocate in the existing block.
127 char *p = _current_chunk.buf + _current_chunk.used;
128 _current_chunk.used += size;
129 return p;
130 }
131
move_memory(memarena * dest)132 void memarena::move_memory(memarena *dest) {
133 // Move memory to dest
134 XREALLOC_N(dest->_n_other_chunks + _n_other_chunks + 1, dest->_other_chunks);
135 dest->_size_of_other_chunks += _size_of_other_chunks + _current_chunk.size;
136 dest->_footprint_of_other_chunks += _footprint_of_other_chunks + toku_memory_footprint(_current_chunk.buf, _current_chunk.used);
137 for (int i = 0; i < _n_other_chunks; i++) {
138 dest->_other_chunks[dest->_n_other_chunks++] = _other_chunks[i];
139 }
140 dest->_other_chunks[dest->_n_other_chunks++] = _current_chunk;
141
142 // Clear out this memarena's memory
143 toku_free(_other_chunks);
144 _current_chunk = arena_chunk();
145 _other_chunks = nullptr;
146 _size_of_other_chunks = 0;
147 _footprint_of_other_chunks = 0;
148 _n_other_chunks = 0;
149 }
150
total_memory_size(void) const151 size_t memarena::total_memory_size(void) const {
152 return sizeof(*this) +
153 total_size_in_use() +
154 _n_other_chunks * sizeof(*_other_chunks);
155 }
156
total_size_in_use(void) const157 size_t memarena::total_size_in_use(void) const {
158 return _size_of_other_chunks + _current_chunk.used;
159 }
160
total_footprint(void) const161 size_t memarena::total_footprint(void) const {
162 return sizeof(*this) +
163 _footprint_of_other_chunks +
164 toku_memory_footprint(_current_chunk.buf, _current_chunk.used) +
165 _n_other_chunks * sizeof(*_other_chunks);
166 }
167
168 ////////////////////////////////////////////////////////////////////////////////
169
current(size_t * used) const170 const void *memarena::chunk_iterator::current(size_t *used) const {
171 if (_chunk_idx < 0) {
172 *used = _ma->_current_chunk.used;
173 return _ma->_current_chunk.buf;
174 } else if (_chunk_idx < _ma->_n_other_chunks) {
175 *used = _ma->_other_chunks[_chunk_idx].used;
176 return _ma->_other_chunks[_chunk_idx].buf;
177 }
178 *used = 0;
179 return nullptr;
180 }
181
next()182 void memarena::chunk_iterator::next() {
183 _chunk_idx++;
184 }
185
more() const186 bool memarena::chunk_iterator::more() const {
187 if (_chunk_idx < 0) {
188 return _ma->_current_chunk.buf != nullptr;
189 }
190 return _chunk_idx < _ma->_n_other_chunks;
191 }
192