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/*======
5This file is part of PerconaFT.
6
7
8Copyright (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
45void 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
58void 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
73static 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
82static const size_t MEMARENA_MAX_CHUNK_SIZE = 64 * 1024 * 1024;
83
84void *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
118void 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
137size_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
143size_t memarena::total_size_in_use(void) const {
144 return _size_of_other_chunks + _current_chunk.used;
145}
146
147size_t memarena::total_footprint(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
156const 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
168void memarena::chunk_iterator::next() {
169 _chunk_idx++;
170}
171
172bool 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