alloc.hpp
12.4 KB
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
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
/*************************************************************************
*
* REALM CONFIDENTIAL
* __________________
*
* [2011] - [2015] Realm Inc
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Realm Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Realm Incorporated
* and its suppliers and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Realm Incorporated.
*
**************************************************************************/
#ifndef REALM_ALLOC_HPP
#define REALM_ALLOC_HPP
#include <stdint.h>
#include <cstddef>
#include <realm/util/features.h>
#include <realm/util/terminate.hpp>
#include <realm/util/assert.hpp>
#include <realm/util/safe_int_ops.hpp>
namespace realm {
class Allocator;
class Replication;
using ref_type = size_t;
int_fast64_t from_ref(ref_type) noexcept;
ref_type to_ref(int_fast64_t) noexcept;
class MemRef {
public:
char* m_addr;
ref_type m_ref;
MemRef() noexcept;
~MemRef() noexcept;
MemRef(char* addr, ref_type ref) noexcept;
MemRef(ref_type ref, Allocator& alloc) noexcept;
};
/// The common interface for Realm allocators.
///
/// A Realm allocator must associate a 'ref' to each allocated
/// object and be able to efficiently map any 'ref' to the
/// corresponding memory address. The 'ref' is an integer and it must
/// always be divisible by 8. Also, a value of zero is used to
/// indicate a null-reference, and must therefore never be returned by
/// Allocator::alloc().
///
/// The purpose of the 'refs' is to decouple the memory reference from
/// the actual address and thereby allowing objects to be relocated in
/// memory without having to modify stored references.
///
/// \sa SlabAlloc
class Allocator {
public:
/// The specified size must be divisible by 8, and must not be
/// zero.
///
/// \throw std::bad_alloc If insufficient memory was available.
MemRef alloc(size_t size);
/// Calls do_realloc().
///
/// Note: The underscore has been added because the name `realloc`
/// would conflict with a macro on the Windows platform.
MemRef realloc_(ref_type, const char* addr, size_t old_size,
size_t new_size);
/// Calls do_free().
///
/// Note: The underscore has been added because the name `free
/// would conflict with a macro on the Windows platform.
void free_(ref_type, const char* addr) noexcept;
/// Shorthand for free_(mem.m_ref, mem.m_addr).
void free_(MemRef mem) noexcept;
/// Calls do_translate().
char* translate(ref_type ref) const noexcept;
/// Returns true if, and only if the object at the specified 'ref'
/// is in the immutable part of the memory managed by this
/// allocator. The method by which some objects become part of the
/// immuatble part is entirely up to the class that implements
/// this interface.
bool is_read_only(ref_type) const noexcept;
/// Returns a simple allocator that can be used with free-standing
/// Realm objects (such as a free-standing table). A
/// free-standing object is one that is not part of a Group, and
/// therefore, is not part of an actual database.
static Allocator& get_default() noexcept;
virtual ~Allocator() noexcept;
#ifdef REALM_DEBUG
virtual void verify() const = 0;
/// Terminate the program precisely when the specified 'ref' is
/// freed (or reallocated). You can use this to detect whether the
/// ref is freed (or reallocated), and even to get a stacktrace at
/// the point where it happens. Call watch(0) to stop watching
/// that ref.
void watch(ref_type);
#endif
Replication* get_replication() noexcept;
/// \brief The version of the format of the the node structure (in file or
/// in memory) in use by Realm objects associated with this allocator.
///
/// Every allocator contains a file format version field, which is returned
/// by this function. In some cases (as mentioned below) the file format can
/// change.
///
/// A value of zero means the the file format is not yet decided. This is
/// only possible for empty Realms where top-ref is zero.
///
/// For the default allocator (get_default()), the file format version field
/// can never change, is never zero, and is set to whatever
/// Group::get_target_file_format_version_for_session() would return if the
/// original file format version was undecided and the request history type
/// was Replication::hist_None.
///
/// For the slab allocator (AllocSlab), the file format version field is set
/// to the file format version specified by the attached file (or attached
/// memory buffer) at the time of attachment. If no file (or buffer) is
/// currently attached, the returned value has no meaning. If the Realm file
/// format is later upgraded, the file form,at version filed must be updated
/// to reflect that fact.
///
/// In shared mode (when a Realm file is opened via a SharedGroup instance)
/// it can happen that the file format is upgraded asyncronously (via
/// another SharedGroup instance), and in that case the file format version
/// field of the allocator can get out of date, but only for a short
/// while. It is always garanteed to be, and remain up to date after the
/// opening process completes (when SharedGroup::do_open() returns).
///
/// An empty Realm file (one whose top-ref is zero) may specify a file
/// format version of zero to indicate that the format is not yet
/// decided. In that case, this function will return zero immediately after
/// AllocSlab::attach_file() returns. It shall be guaranteed, however, that
/// the zero is changed to a proper file format version before the opening
/// process completes (Group::open() or SharedGroup::open()). It is the duty
/// of the caller of AllocSlab::attach_file() to ensure this.
///
/// File format versions:
///
/// 1 Initial file format version
///
/// 2 FIXME: Does anybody remember what happened here?
///
/// 3 Supporting null on string columns broke the file format in following
/// way: Index appends an 'X' character to all strings except the null
/// string, to be able to distinguish between null and empty
/// string. Bumped to 3 because of null support of String columns and
/// because of new format of index.
///
/// 4 Introduction of optional in-Realm history of changes (additional
/// entries in Group::m_top). Since this change is not forward
/// compatible, the file format version had to be bumped. This change is
/// implemented in a way that achieves backwards compatibility with
/// version 3 (and in turn with version 2).
///
/// IMPORTANT: When introducing a new file format version, be sure to review
/// the file validity checks in AllocSlab::validate_buffer(), the file
/// format selection loginc in
/// Group::get_target_file_format_version_for_session(), and the file format
/// upgrade logic in Group::upgrade_file_format().
int get_file_format_version() const noexcept;
protected:
size_t m_baseline = 0; // Separation line between immutable and mutable refs.
Replication* m_replication;
/// See get_file_format_version().
int m_file_format_version = 0;
#ifdef REALM_DEBUG
ref_type m_watch;
#endif
/// The specified size must be divisible by 8, and must not be
/// zero.
///
/// \throw std::bad_alloc If insufficient memory was available.
virtual MemRef do_alloc(size_t size) = 0;
/// The specified size must be divisible by 8, and must not be
/// zero.
///
/// The default version of this function simply allocates a new
/// chunk of memory, copies over the old contents, and then frees
/// the old chunk.
///
/// \throw std::bad_alloc If insufficient memory was available.
virtual MemRef do_realloc(ref_type, const char* addr, size_t old_size,
size_t new_size);
/// Release the specified chunk of memory.
virtual void do_free(ref_type, const char* addr) noexcept = 0;
/// Map the specified \a ref to the corresponding memory
/// address. Note that if is_read_only(ref) returns true, then the
/// referenced object is to be considered immutable, and it is
/// then entirely the responsibility of the caller that the memory
/// is not modified by way of the returned memory pointer.
virtual char* do_translate(ref_type ref) const noexcept = 0;
Allocator() noexcept;
// FIXME: This really doesn't belong in an allocator, but it is the best
// place for now, because every table has a pointer leading here. It would
// be more obvious to place it in Group, but that would add a runtime overhead,
// and access is time critical.
uint_fast64_t m_table_versioning_counter;
/// Bump the global version counter. This method should be called when
/// version bumping is initiated. Then following calls to should_propagate_version()
/// can be used to prune the version bumping.
uint_fast64_t bump_global_version() noexcept;
/// Determine if the "local_version" is out of sync, so that it should
/// be updated. In that case: also update it. Called from Table::bump_version
/// to control propagation of version updates on tables within the group.
bool should_propagate_version(uint_fast64_t& local_version) noexcept;
friend class Table;
friend class Group;
};
inline uint_fast64_t Allocator::bump_global_version() noexcept
{
++m_table_versioning_counter;
return m_table_versioning_counter;
}
inline bool Allocator::should_propagate_version(uint_fast64_t& local_version) noexcept
{
if (local_version != m_table_versioning_counter) {
local_version = m_table_versioning_counter;
return true;
}
else {
return false;
}
}
// Implementation:
inline int_fast64_t from_ref(ref_type v) noexcept
{
// Check that v is divisible by 8 (64-bit aligned).
REALM_ASSERT_DEBUG(v % 8 == 0);
return util::from_twos_compl<int_fast64_t>(v);
}
inline ref_type to_ref(int_fast64_t v) noexcept
{
REALM_ASSERT_DEBUG(!util::int_cast_has_overflow<ref_type>(v));
// Check that v is divisible by 8 (64-bit aligned).
REALM_ASSERT_DEBUG(v % 8 == 0);
return ref_type(v);
}
inline MemRef::MemRef() noexcept:
m_addr(nullptr),
m_ref(0)
{
}
inline MemRef::~MemRef() noexcept
{
}
inline MemRef::MemRef(char* addr, ref_type ref) noexcept:
m_addr(addr),
m_ref(ref)
{
}
inline MemRef::MemRef(ref_type ref, Allocator& alloc) noexcept:
m_addr(alloc.translate(ref)),
m_ref(ref)
{
}
inline MemRef Allocator::alloc(size_t size)
{
return do_alloc(size);
}
inline MemRef Allocator::realloc_(ref_type ref, const char* addr, size_t old_size,
size_t new_size)
{
#ifdef REALM_DEBUG
if (ref == m_watch)
REALM_TERMINATE("Allocator watch: Ref was reallocated");
#endif
return do_realloc(ref, addr, old_size, new_size);
}
inline void Allocator::free_(ref_type ref, const char* addr) noexcept
{
#ifdef REALM_DEBUG
if (ref == m_watch)
REALM_TERMINATE("Allocator watch: Ref was freed");
#endif
return do_free(ref, addr);
}
inline void Allocator::free_(MemRef mem) noexcept
{
free_(mem.m_ref, mem.m_addr);
}
inline char* Allocator::translate(ref_type ref) const noexcept
{
return do_translate(ref);
}
inline bool Allocator::is_read_only(ref_type ref) const noexcept
{
REALM_ASSERT_DEBUG(ref != 0);
REALM_ASSERT_DEBUG(m_baseline != 0); // Attached SlabAlloc
return ref < m_baseline;
}
inline Allocator::Allocator() noexcept:
m_replication(nullptr)
{
#ifdef REALM_DEBUG
m_watch = 0;
#endif
m_table_versioning_counter = 0;
}
inline Allocator::~Allocator() noexcept
{
}
inline Replication* Allocator::get_replication() noexcept
{
return m_replication;
}
#ifdef REALM_DEBUG
inline void Allocator::watch(ref_type ref)
{
m_watch = ref;
}
#endif
inline int Allocator::get_file_format_version() const noexcept
{
return m_file_format_version;
}
} // namespace realm
#endif // REALM_ALLOC_HPP