1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #ifndef RUNTIME_VM_ZONE_H_ |
6 | #define RUNTIME_VM_ZONE_H_ |
7 | |
8 | #include "platform/utils.h" |
9 | #include "vm/allocation.h" |
10 | #include "vm/handles.h" |
11 | #include "vm/memory_region.h" |
12 | #include "vm/thread_state.h" |
13 | |
14 | namespace dart { |
15 | |
16 | // Zones support very fast allocation of small chunks of memory. The |
17 | // chunks cannot be deallocated individually, but instead zones |
18 | // support deallocating all chunks in one fast operation. |
19 | |
20 | class Zone { |
21 | public: |
22 | // Allocate an array sized to hold 'len' elements of type |
23 | // 'ElementType'. Checks for integer overflow when performing the |
24 | // size computation. |
25 | template <class ElementType> |
26 | inline ElementType* Alloc(intptr_t len); |
27 | |
28 | // Allocates an array sized to hold 'len' elements of type |
29 | // 'ElementType'. The new array is initialized from the memory of |
30 | // 'old_array' up to 'old_len'. |
31 | template <class ElementType> |
32 | inline ElementType* Realloc(ElementType* old_array, |
33 | intptr_t old_len, |
34 | intptr_t new_len); |
35 | |
36 | // Allocates 'size' bytes of memory in the zone; expands the zone by |
37 | // allocating new segments of memory on demand using 'new'. |
38 | // |
39 | // It is preferred to use Alloc<T>() instead, as that function can |
40 | // check for integer overflow. If you use AllocUnsafe, you are |
41 | // responsible for avoiding integer overflow yourself. |
42 | inline uword AllocUnsafe(intptr_t size); |
43 | |
44 | // Make a copy of the string in the zone allocated area. |
45 | char* MakeCopyOfString(const char* str); |
46 | |
47 | // Make a copy of the first n characters of a string in the zone |
48 | // allocated area. |
49 | char* MakeCopyOfStringN(const char* str, intptr_t len); |
50 | |
51 | // Concatenate strings |a| and |b|. |a| may be NULL. If |a| is not NULL, |
52 | // |join| will be inserted between |a| and |b|. |
53 | char* ConcatStrings(const char* a, const char* b, char join = ','); |
54 | |
55 | // TODO(zra): Remove these calls and replace them with calls to OS::SCreate |
56 | // and OS::VSCreate. |
57 | // These calls are deprecated. Do not add further calls to these functions. |
58 | // instead use OS::SCreate and OS::VSCreate. |
59 | // Make a zone-allocated string based on printf format and args. |
60 | char* PrintToString(const char* format, ...) PRINTF_ATTRIBUTE(2, 3); |
61 | char* VPrint(const char* format, va_list args); |
62 | |
63 | // Compute the total size of this zone. This includes wasted space that is |
64 | // due to internal fragmentation in the segments. |
65 | uintptr_t SizeInBytes() const; |
66 | |
67 | // Computes the amount of space used in the zone. |
68 | uintptr_t CapacityInBytes() const; |
69 | |
70 | // Structure for managing handles allocation. |
71 | VMHandles* handles() { return &handles_; } |
72 | |
73 | void VisitObjectPointers(ObjectPointerVisitor* visitor); |
74 | |
75 | Zone* previous() const { return previous_; } |
76 | |
77 | bool ContainsNestedZone(Zone* other) const { |
78 | while (other != NULL) { |
79 | if (this == other) return true; |
80 | other = other->previous_; |
81 | } |
82 | return false; |
83 | } |
84 | |
85 | // All pointers returned from AllocateUnsafe() and New() have this alignment. |
86 | static const intptr_t kAlignment = kDoubleSize; |
87 | |
88 | static void Init(); |
89 | static void Cleanup(); |
90 | |
91 | static intptr_t Size() { return total_size_; } |
92 | |
93 | private: |
94 | Zone(); |
95 | ~Zone(); // Delete all memory associated with the zone. |
96 | |
97 | // Default initial chunk size. |
98 | static const intptr_t kInitialChunkSize = 1 * KB; |
99 | |
100 | // Default segment size. |
101 | static const intptr_t kSegmentSize = 64 * KB; |
102 | |
103 | // Zap value used to indicate deleted zone area (debug purposes). |
104 | static const unsigned char kZapDeletedByte = 0x42; |
105 | |
106 | // Zap value used to indicate uninitialized zone area (debug purposes). |
107 | static const unsigned char kZapUninitializedByte = 0xab; |
108 | |
109 | // Total size of current zone segments. |
110 | static RelaxedAtomic<intptr_t> total_size_; |
111 | |
112 | // Expand the zone to accommodate an allocation of 'size' bytes. |
113 | uword AllocateExpand(intptr_t size); |
114 | |
115 | // Allocate a large segment. |
116 | uword AllocateLargeSegment(intptr_t size); |
117 | |
118 | // Insert zone into zone chain, after current_zone. |
119 | void Link(Zone* current_zone) { previous_ = current_zone; } |
120 | |
121 | // Delete all objects and free all memory allocated in the zone. |
122 | void DeleteAll(); |
123 | |
124 | // Does not actually free any memory. Enables templated containers like |
125 | // BaseGrowableArray to use different allocators. |
126 | template <class ElementType> |
127 | void Free(ElementType* old_array, intptr_t len) { |
128 | #ifdef DEBUG |
129 | if (len > 0) { |
130 | ASSERT(old_array != nullptr); |
131 | memset(static_cast<void*>(old_array), kZapUninitializedByte, |
132 | len * sizeof(ElementType)); |
133 | } |
134 | #endif |
135 | } |
136 | |
137 | // Dump the current allocated sizes in the zone object. |
138 | void DumpZoneSizes(); |
139 | |
140 | // Overflow check (FATAL) for array length. |
141 | template <class ElementType> |
142 | static inline void CheckLength(intptr_t len); |
143 | |
144 | // This buffer is used for allocation before any segments. |
145 | // This would act as the initial stack allocated chunk so that we don't |
146 | // end up calling malloc/free on zone scopes that allocate less than |
147 | // kChunkSize |
148 | COMPILE_ASSERT(kAlignment <= 8); |
149 | ALIGN8 uint8_t buffer_[kInitialChunkSize]; |
150 | MemoryRegion initial_buffer_; |
151 | |
152 | // The free region in the current (head) segment or the initial buffer is |
153 | // represented as the half-open interval [position, limit). The 'position' |
154 | // variable is guaranteed to be aligned as dictated by kAlignment. |
155 | uword position_; |
156 | uword limit_; |
157 | |
158 | // Zone segments are internal data structures used to hold information |
159 | // about the memory segmentations that constitute a zone. The entire |
160 | // implementation is in zone.cc. |
161 | class Segment; |
162 | |
163 | // Total size of all segments in [head_]. |
164 | intptr_t small_segment_capacity_ = 0; |
165 | |
166 | // The current head segment; may be NULL. |
167 | Segment* head_; |
168 | |
169 | // List of large segments allocated in this zone; may be NULL. |
170 | Segment* large_segments_; |
171 | |
172 | // Structure for managing handles allocation. |
173 | VMHandles handles_; |
174 | |
175 | // Used for chaining zones in order to allow unwinding of stacks. |
176 | Zone* previous_; |
177 | |
178 | friend class StackZone; |
179 | friend class ApiZone; |
180 | template <typename T, typename B, typename Allocator> |
181 | friend class BaseGrowableArray; |
182 | template <typename T, typename B, typename Allocator> |
183 | friend class BaseDirectChainedHashMap; |
184 | DISALLOW_COPY_AND_ASSIGN(Zone); |
185 | }; |
186 | |
187 | class StackZone : public StackResource { |
188 | public: |
189 | // Create an empty zone and set is at the current zone for the Thread. |
190 | explicit StackZone(ThreadState* thread); |
191 | |
192 | // Delete all memory associated with the zone. |
193 | virtual ~StackZone(); |
194 | |
195 | // Compute the total size of this zone. This includes wasted space that is |
196 | // due to internal fragmentation in the segments. |
197 | uintptr_t SizeInBytes() const { return zone_->SizeInBytes(); } |
198 | |
199 | // Computes the used space in the zone. |
200 | intptr_t CapacityInBytes() const { return zone_->CapacityInBytes(); } |
201 | |
202 | Zone* GetZone() { return zone_; } |
203 | |
204 | private: |
205 | Zone* zone_; |
206 | |
207 | template <typename T> |
208 | friend class GrowableArray; |
209 | template <typename T> |
210 | friend class ZoneGrowableArray; |
211 | |
212 | DISALLOW_IMPLICIT_CONSTRUCTORS(StackZone); |
213 | }; |
214 | |
215 | inline uword Zone::AllocUnsafe(intptr_t size) { |
216 | ASSERT(size >= 0); |
217 | // Round up the requested size to fit the alignment. |
218 | if (size > (kIntptrMax - kAlignment)) { |
219 | FATAL1("Zone::Alloc: 'size' is too large: size=%" Pd "" , size); |
220 | } |
221 | size = Utils::RoundUp(size, kAlignment); |
222 | |
223 | // Check if the requested size is available without expanding. |
224 | uword result; |
225 | intptr_t free_size = (limit_ - position_); |
226 | if (free_size >= size) { |
227 | result = position_; |
228 | position_ += size; |
229 | } else { |
230 | result = AllocateExpand(size); |
231 | } |
232 | |
233 | // Check that the result has the proper alignment and return it. |
234 | ASSERT(Utils::IsAligned(result, kAlignment)); |
235 | return result; |
236 | } |
237 | |
238 | template <class ElementType> |
239 | inline void Zone::CheckLength(intptr_t len) { |
240 | const intptr_t kElementSize = sizeof(ElementType); |
241 | if (len > (kIntptrMax / kElementSize)) { |
242 | FATAL2("Zone::Alloc: 'len' is too large: len=%" Pd ", kElementSize=%" Pd, |
243 | len, kElementSize); |
244 | } |
245 | } |
246 | |
247 | template <class ElementType> |
248 | inline ElementType* Zone::Alloc(intptr_t len) { |
249 | CheckLength<ElementType>(len); |
250 | return reinterpret_cast<ElementType*>(AllocUnsafe(len * sizeof(ElementType))); |
251 | } |
252 | |
253 | template <class ElementType> |
254 | inline ElementType* Zone::Realloc(ElementType* old_data, |
255 | intptr_t old_len, |
256 | intptr_t new_len) { |
257 | CheckLength<ElementType>(new_len); |
258 | const intptr_t kElementSize = sizeof(ElementType); |
259 | uword old_end = reinterpret_cast<uword>(old_data) + (old_len * kElementSize); |
260 | // Resize existing allocation if nothing was allocated in between... |
261 | if (Utils::RoundUp(old_end, kAlignment) == position_) { |
262 | uword new_end = |
263 | reinterpret_cast<uword>(old_data) + (new_len * kElementSize); |
264 | // ...and there is sufficient space. |
265 | if (new_end <= limit_) { |
266 | ASSERT(new_len >= old_len); |
267 | position_ = Utils::RoundUp(new_end, kAlignment); |
268 | return old_data; |
269 | } |
270 | } |
271 | if (new_len <= old_len) { |
272 | return old_data; |
273 | } |
274 | ElementType* new_data = Alloc<ElementType>(new_len); |
275 | if (old_data != 0) { |
276 | memmove(reinterpret_cast<void*>(new_data), |
277 | reinterpret_cast<void*>(old_data), old_len * kElementSize); |
278 | } |
279 | return new_data; |
280 | } |
281 | |
282 | } // namespace dart |
283 | |
284 | #endif // RUNTIME_VM_ZONE_H_ |
285 | |