-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathzone.h
330 lines (265 loc) · 10.7 KB
/
zone.h
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
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_ZONE_H_
#define RUNTIME_VM_ZONE_H_
#include "platform/utils.h"
#include "vm/allocation.h"
#include "vm/handles.h"
#include "vm/memory_region.h"
#include "vm/thread_state.h"
namespace dart {
// Zones support very fast allocation of small chunks of memory. The
// chunks cannot be deallocated individually, but instead zones
// support deallocating all chunks in one fast operation.
class Zone {
public:
// Allocate an array sized to hold 'len' elements of type
// 'ElementType'. Checks for integer overflow when performing the
// size computation.
template <class ElementType>
inline ElementType* Alloc(intptr_t len);
// Allocates an array sized to hold 'len' elements of type
// 'ElementType'. The new array is initialized from the memory of
// 'old_array' up to 'old_len'.
template <class ElementType>
inline ElementType* Realloc(ElementType* old_array,
intptr_t old_len,
intptr_t new_len);
// Allocates 'size' bytes of memory in the zone; expands the zone by
// allocating new segments of memory on demand using 'new'.
//
// It is preferred to use Alloc<T>() instead, as that function can
// check for integer overflow. If you use AllocUnsafe, you are
// responsible for avoiding integer overflow yourself.
inline uword AllocUnsafe(intptr_t size);
// Make a copy of the string in the zone allocated area.
char* MakeCopyOfString(const char* str);
// Make a copy of the first n characters of a string in the zone
// allocated area.
char* MakeCopyOfStringN(const char* str, intptr_t len);
// Concatenate strings |a| and |b|. |a| may be nullptr. If |a| is not nullptr,
// |join| will be inserted between |a| and |b|.
char* ConcatStrings(const char* a, const char* b, char join = ',');
// TODO(zra): Remove these calls and replace them with calls to OS::SCreate
// and OS::VSCreate.
// These calls are deprecated. Do not add further calls to these functions.
// instead use OS::SCreate and OS::VSCreate.
// Make a zone-allocated string based on printf format and args.
char* PrintToString(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
char* VPrint(const char* format, va_list args);
// Compute the total size of allocations in this zone.
uintptr_t SizeInBytes() const;
// Computes the amount of space used by the zone.
uintptr_t CapacityInBytes() const;
// Dump the current allocated sizes in the zone object.
void Print() const;
// Structure for managing handles allocation.
VMHandles* handles() { return &handles_; }
void VisitObjectPointers(ObjectPointerVisitor* visitor);
Zone* previous() const { return previous_; }
bool ContainsNestedZone(Zone* other) const {
while (other != nullptr) {
if (this == other) return true;
other = other->previous_;
}
return false;
}
// All pointers returned from AllocateUnsafe() and New() have this alignment.
static constexpr intptr_t kAlignment = kDoubleSize;
static void Init();
static void Cleanup();
static void ClearCache();
static intptr_t Size() { return total_size_; }
// Allow templated containers to check if this allocator supports
// freeing individual allocations.
static constexpr bool kSupportsFreeingIndividualAllocations = false;
private:
Zone();
~Zone(); // Delete all memory associated with the zone.
// Default initial chunk size.
static constexpr intptr_t kInitialChunkSize = 128;
// Default segment size.
static constexpr intptr_t kSegmentSize = 64 * KB;
// Zap value used to indicate deleted zone area (debug purposes).
static constexpr unsigned char kZapDeletedByte = 0x42;
// Zap value used to indicate uninitialized zone area (debug purposes).
static constexpr unsigned char kZapUninitializedByte = 0xab;
// Total size of current zone segments.
static RelaxedAtomic<intptr_t> total_size_;
// Expand the zone to accommodate an allocation of 'size' bytes.
uword AllocateExpand(intptr_t size);
// Allocate a large segment.
uword AllocateLargeSegment(intptr_t size);
// Insert zone into zone chain, after current_zone.
void Link(Zone* current_zone) { previous_ = current_zone; }
// Delete all objects and free all memory allocated in the zone.
void Reset();
// Does not actually free any memory. Enables templated containers like
// BaseGrowableArray to use different allocators.
template <class ElementType>
void Free(ElementType* old_array, intptr_t len) {
#ifdef DEBUG
if (len > 0) {
ASSERT(old_array != nullptr);
memset(static_cast<void*>(old_array), kZapUninitializedByte,
len * sizeof(ElementType));
}
#endif
}
// Overflow check (FATAL) for array length.
template <class ElementType>
static inline void CheckLength(intptr_t len);
// The free region in the current (head) segment or the initial buffer is
// represented as the half-open interval [position, limit). The 'position'
// variable is guaranteed to be aligned as dictated by kAlignment.
uword position_;
uword limit_;
// Zone segments are internal data structures used to hold information
// about the memory segmentations that constitute a zone. The entire
// implementation is in zone.cc.
class Segment;
// Total size of all allocations in this zone.
intptr_t size_ = 0;
// Total size of all segments in [head_].
intptr_t small_segment_capacity_ = 0;
// List of all segments allocated in this zone; may be nullptr.
Segment* segments_;
// Used for chaining zones in order to allow unwinding of stacks.
Zone* previous_;
// Structure for managing handles allocation.
VMHandles handles_;
// This buffer is used for allocation before any segments.
// This would act as the initial stack allocated chunk so that we don't
// end up calling malloc/free on zone scopes that allocate less than
// kChunkSize
COMPILE_ASSERT(kAlignment <= 8);
ALIGN8 uint8_t buffer_[kInitialChunkSize];
friend class StackZone;
friend class ApiZone;
friend class AllocOnlyStackZone;
template <typename T, typename B, typename Allocator>
friend class BaseGrowableArray;
template <typename T, typename B, typename Allocator>
friend class BaseDirectChainedHashMap;
DISALLOW_COPY_AND_ASSIGN(Zone);
};
class StackZone : public StackResource {
public:
// Create an empty zone and set is at the current zone for the Thread.
explicit StackZone(ThreadState* thread);
// Delete all memory associated with the zone.
virtual ~StackZone();
// DART_USE_ABSL encodes the use of fibers in the Dart VM for threading,
#if defined(DART_USE_ABSL)
// Compute the total size of this zone. This includes wasted space that is
// due to internal fragmentation in the segments.
uintptr_t SizeInBytes() const { return zone_->SizeInBytes(); }
// Computes the used space in the zone.
intptr_t CapacityInBytes() const { return zone_->CapacityInBytes(); }
Zone* GetZone() { return zone_; }
#else
// Compute the total size of this zone. This includes wasted space that is
// due to internal fragmentation in the segments.
uintptr_t SizeInBytes() const { return zone_.SizeInBytes(); }
// Computes the used space in the zone.
intptr_t CapacityInBytes() const { return zone_.CapacityInBytes(); }
Zone* GetZone() { return &zone_; }
#endif // defined(DART_USE_ABSL)
private:
#if defined(DART_USE_ABSL)
// When fibers are used we have to make do with a smaller stack size and hence
// the first zone is allocated instead of being a stack resource.
Zone* zone_;
#else
// For regular configurations that have larger stack sizes it is ok to
// have the first zone be a stack resource, avoids the overhead of a malloc
// call for every stack zone creation.
Zone zone_;
#endif // defined(DART_USE_ABSL)
template <typename T>
friend class GrowableArray;
template <typename T>
friend class ZoneGrowableArray;
DISALLOW_IMPLICIT_CONSTRUCTORS(StackZone);
};
class AllocOnlyStackZone : public ValueObject {
public:
AllocOnlyStackZone() : zone_() {}
~AllocOnlyStackZone() {
// This zone is not linked into the thread, so any handles would not be
// visited.
ASSERT(zone_.handles()->IsEmpty());
}
Zone* GetZone() { return &zone_; }
private:
Zone zone_;
DISALLOW_COPY_AND_ASSIGN(AllocOnlyStackZone);
};
inline uword Zone::AllocUnsafe(intptr_t size) {
ASSERT(size >= 0);
// Round up the requested size to fit the alignment.
if (size > (kIntptrMax - kAlignment)) {
FATAL("Zone::Alloc: 'size' is too large: size=%" Pd "", size);
}
size = Utils::RoundUp(size, kAlignment);
// Check if the requested size is available without expanding.
uword result;
intptr_t free_size = (limit_ - position_);
if (free_size >= size) {
result = position_;
position_ += size;
size_ += size;
} else {
result = AllocateExpand(size);
}
// Check that the result has the proper alignment and return it.
ASSERT(Utils::IsAligned(result, kAlignment));
return result;
}
template <class ElementType>
inline void Zone::CheckLength(intptr_t len) {
const intptr_t kElementSize = sizeof(ElementType);
if (len > (kIntptrMax / kElementSize)) {
FATAL("Zone::Alloc: 'len' is too large: len=%" Pd ", kElementSize=%" Pd,
len, kElementSize);
}
}
template <class ElementType>
inline ElementType* Zone::Alloc(intptr_t len) {
CheckLength<ElementType>(len);
return reinterpret_cast<ElementType*>(AllocUnsafe(len * sizeof(ElementType)));
}
template <class ElementType>
inline ElementType* Zone::Realloc(ElementType* old_data,
intptr_t old_len,
intptr_t new_len) {
CheckLength<ElementType>(new_len);
const intptr_t kElementSize = sizeof(ElementType);
if (old_data != nullptr) {
uword old_end =
reinterpret_cast<uword>(old_data) + (old_len * kElementSize);
// Resize existing allocation if nothing was allocated in between...
if (Utils::RoundUp(old_end, kAlignment) == position_) {
uword new_end =
reinterpret_cast<uword>(old_data) + (new_len * kElementSize);
// ...and there is sufficient space.
if (new_end <= limit_) {
position_ = Utils::RoundUp(new_end, kAlignment);
size_ += static_cast<intptr_t>(new_len - old_len);
return old_data;
}
}
if (new_len <= old_len) {
return old_data;
}
}
ElementType* new_data = Alloc<ElementType>(new_len);
if (old_data != nullptr) {
memmove(reinterpret_cast<void*>(new_data),
reinterpret_cast<void*>(old_data), old_len * kElementSize);
}
return new_data;
}
} // namespace dart
#endif // RUNTIME_VM_ZONE_H_