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