1// Copyright (c) 2011, 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_HANDLES_H_
6#define RUNTIME_VM_HANDLES_H_
7
8#include "vm/allocation.h"
9#include "vm/flags.h"
10#include "vm/os.h"
11
12namespace dart {
13
14// Handles are used in the Dart Virtual Machine to ensure that access
15// to dart objects in the virtual machine code is done in a
16// Garbage Collection safe manner.
17//
18// The class Handles is the basic type that implements creation of handles and
19// manages their life cycle (allocated either in the current zone or
20// current handle scope).
21// The two forms of handle allocation are:
22// - allocation of handles in the current zone (Handle::AllocateZoneHandle).
23// Handles allocated in this manner are destroyed when the zone is destroyed.
24// - allocation of handles in a scoped manner (Handle::AllocateHandle).
25// A new scope can be started using HANDLESCOPE(thread).
26// Handles allocated in this manner are destroyed when the HandleScope
27// object is destroyed.
28// Code that uses scoped handles typically looks as follows:
29// {
30// HANDLESCOPE(thread);
31// const String& str = String::Handle(String::New("abc"));
32// .....
33// .....
34// }
35// Code that uses zone handles typically looks as follows:
36// const String& str = String::ZoneHandle(String::New("abc"));
37// .....
38// .....
39//
40// The Handle function for each object type internally uses the
41// Handles::AllocateHandle() function for creating handles. The Handle
42// function of the object type is the only way to create scoped handles
43// in the dart VM.
44// The ZoneHandle function for each object type internally uses the
45// Handles::AllocateZoneHandle() function for creating zone handles.
46// The ZoneHandle function of the object type is the only way to create
47// zone handles in the dart VM.
48
49// Forward declarations.
50class ObjectPointerVisitor;
51class HandleVisitor;
52
53DECLARE_FLAG(bool, verify_handles);
54
55template <int kHandleSizeInWords, int kHandlesPerChunk, int kOffsetOfRawPtr>
56class Handles {
57 public:
58 Handles()
59 : zone_blocks_(NULL),
60 first_scoped_block_(NULL),
61 scoped_blocks_(&first_scoped_block_) {}
62 ~Handles() { DeleteAll(); }
63
64 // Visit all object pointers stored in the various handles.
65 void VisitObjectPointers(ObjectPointerVisitor* visitor);
66
67 // Visit all the scoped handles.
68 void VisitScopedHandles(ObjectPointerVisitor* visitor);
69
70 // Visit all blocks that have been added since the last time
71 // this method was called.
72 // Be careful with this, since multiple users of this method could
73 // interfere with eachother.
74 // Currently only used by GC trace facility.
75 void VisitUnvisitedScopedHandles(ObjectPointerVisitor* visitor);
76
77 // Visit all of the various handles.
78 void Visit(HandleVisitor* visitor);
79
80 // Reset the handles so that we can reuse.
81 void Reset();
82
83 // Allocates a handle in the current handle scope. This handle is valid only
84 // in the current handle scope and is destroyed when the current handle
85 // scope ends.
86 static uword AllocateHandle(Zone* zone);
87
88 // Allocates a handle in the current zone. This handle will be destroyed
89 // when the current zone is destroyed.
90 static uword AllocateZoneHandle(Zone* zone);
91
92 // Returns true if specified handle is a zone handle.
93 static bool IsZoneHandle(uword handle);
94
95 // Allocates space for a scoped handle.
96 uword AllocateScopedHandle() {
97 if (scoped_blocks_->IsFull()) {
98 SetupNextScopeBlock();
99 }
100 return scoped_blocks_->AllocateHandle();
101 }
102
103 protected:
104 // Returns a count of active handles (used for testing purposes).
105 int CountScopedHandles() const;
106 int CountZoneHandles() const;
107
108 // Returns true if passed in handle is a valid zone handle.
109 bool IsValidScopedHandle(uword handle) const;
110 bool IsValidZoneHandle(uword handle) const;
111
112 private:
113 // Base structure for managing blocks of handles.
114 // Handles are allocated in Chunks (each chunk holds kHandlesPerChunk
115 // handles). The chunk is uninitialized, subsequent requests for handles
116 // is allocated from the chunk until we run out space in the chunk,
117 // at this point another chunk is allocated. These chunks are chained
118 // together.
119 class HandlesBlock {
120 public:
121 explicit HandlesBlock(HandlesBlock* next)
122 : next_handle_slot_(0), next_block_(next) {}
123 ~HandlesBlock();
124
125 // Reinitializes handle block for reuse.
126 void ReInit();
127
128 // Returns true if the handle block is full.
129 bool IsFull() const {
130 return next_handle_slot_ >= (kHandleSizeInWords * kHandlesPerChunk);
131 }
132
133 // Returns true if passed in handle belongs to this block.
134 bool IsValidHandle(uword handle) const {
135 uword start = reinterpret_cast<uword>(data_);
136 uword end = start + (kHandleSizeInWords * kWordSize * kHandlesPerChunk);
137 return (start <= handle && handle < end);
138 }
139
140 // Allocates space for a handle in the data area.
141 uword AllocateHandle() {
142 ASSERT(!IsFull());
143 uword handle_address = reinterpret_cast<uword>(data_ + next_handle_slot_);
144 next_handle_slot_ += kHandleSizeInWords;
145 return handle_address;
146 }
147
148 // Visit all object pointers in the handle block.
149 void VisitObjectPointers(ObjectPointerVisitor* visitor);
150
151 // Visit all of the handles in the handle block.
152 void Visit(HandleVisitor* visitor);
153
154#if defined(DEBUG)
155 // Zaps the free handle area to an uninitialized value.
156 void ZapFreeHandles();
157#endif
158
159 // Returns number of active handles in the handle block.
160 int HandleCount() const;
161
162 // Accessors.
163 intptr_t next_handle_slot() const { return next_handle_slot_; }
164 void set_next_handle_slot(intptr_t next_handle_slot) {
165 next_handle_slot_ = next_handle_slot;
166 }
167 HandlesBlock* next_block() const { return next_block_; }
168 void set_next_block(HandlesBlock* next) { next_block_ = next; }
169
170 private:
171 uword data_[kHandleSizeInWords * kHandlesPerChunk]; // Handles area.
172 intptr_t next_handle_slot_; // Next slot for allocation in current block.
173 HandlesBlock* next_block_; // Link to next block of handles.
174
175 DISALLOW_COPY_AND_ASSIGN(HandlesBlock);
176 };
177
178 // Deletes all the allocated handle blocks.
179 void DeleteAll();
180 void DeleteHandleBlocks(HandlesBlock* blocks);
181
182 // Sets up the next handle block (allocates a new one if needed).
183 void SetupNextScopeBlock();
184
185 // Allocates space for a zone handle.
186 uword AllocateHandleInZone() {
187 if (zone_blocks_ == NULL || zone_blocks_->IsFull()) {
188 SetupNextZoneBlock();
189 }
190 return zone_blocks_->AllocateHandle();
191 }
192
193 // Allocates a new handle block and links it up.
194 void SetupNextZoneBlock();
195
196#if defined(DEBUG)
197 // Verifies consistency of handle blocks after a scope is destroyed.
198 void VerifyScopedHandleState();
199
200 // Zaps the free scoped handles to an uninitialized value.
201 void ZapFreeScopedHandles();
202#endif
203
204 HandlesBlock* zone_blocks_; // List of zone handles.
205 HandlesBlock first_scoped_block_; // First block of scoped handles.
206 HandlesBlock* scoped_blocks_; // List of scoped handles.
207
208 friend class HandleScope;
209 friend class Dart;
210 friend class IsolateObjectStore;
211 friend class ObjectStore;
212 friend class ThreadState;
213 DISALLOW_ALLOCATION();
214 DISALLOW_COPY_AND_ASSIGN(Handles);
215};
216
217static const int kVMHandleSizeInWords = 2;
218static const int kVMHandlesPerChunk = 64;
219static const int kOffsetOfRawPtr = kWordSize;
220class VMHandles : public Handles<kVMHandleSizeInWords,
221 kVMHandlesPerChunk,
222 kOffsetOfRawPtr> {
223 public:
224 static const int kOffsetOfRawPtrInHandle = kOffsetOfRawPtr;
225
226 VMHandles()
227 : Handles<kVMHandleSizeInWords, kVMHandlesPerChunk, kOffsetOfRawPtr>() {
228 if (FLAG_trace_handles) {
229 OS::PrintErr("*** Starting a new VM handle block 0x%" Px "\n",
230 reinterpret_cast<intptr_t>(this));
231 }
232 }
233 ~VMHandles();
234
235 // Visit all object pointers stored in the various handles.
236 void VisitObjectPointers(ObjectPointerVisitor* visitor);
237
238 // Allocates a handle in the current handle scope of 'zone', which must be
239 // the current zone. This handle is valid only in the current handle scope
240 // and is destroyed when the current handle scope ends.
241 static uword AllocateHandle(Zone* zone);
242
243 // Allocates a handle in 'zone', which must be the current zone. This handle
244 // will be destroyed when the current zone is destroyed.
245 static uword AllocateZoneHandle(Zone* zone);
246
247 // Returns true if specified handle is a zone handle.
248 static bool IsZoneHandle(uword handle);
249
250 // Returns number of handles, these functions are used for testing purposes.
251 static int ScopedHandleCount();
252 static int ZoneHandleCount();
253
254 friend class ApiZone;
255 friend class ApiNativeScope;
256};
257
258// The class HandleScope is used to start a new handles scope in the code.
259// It is used as follows:
260// {
261// HANDLESCOPE(thread);
262// ....
263// .....
264// code that creates some scoped handles.
265// ....
266// }
267class HandleScope : public StackResource {
268 public:
269 explicit HandleScope(ThreadState* thread);
270 ~HandleScope();
271
272 private:
273 void Initialize();
274
275 VMHandles::HandlesBlock* saved_handle_block_; // Handle block at prev scope.
276 uword saved_handle_slot_; // Next available handle slot at previous scope.
277#if defined(DEBUG)
278 HandleScope* link_; // Link to previous scope.
279#endif
280 DISALLOW_IMPLICIT_CONSTRUCTORS(HandleScope);
281};
282
283// Macro to start a new Handle scope.
284#define HANDLESCOPE(thread) \
285 dart::HandleScope vm_internal_handles_scope_(thread);
286
287} // namespace dart
288
289#endif // RUNTIME_VM_HANDLES_H_
290