1 // Copyright 2009 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #include "v8.h"
29
30 #include "api.h"
31 #include "global-handles.h"
32
33 namespace v8 {
34 namespace internal {
35
36 class GlobalHandles::Node : public Malloced {
37 public:
38
Initialize(Object * object)39 void Initialize(Object* object) {
40 // Set the initial value of the handle.
41 object_ = object;
42 state_ = NORMAL;
43 parameter_or_next_free_.parameter = NULL;
44 callback_ = NULL;
45 }
46
Node(Object * object)47 explicit Node(Object* object) {
48 Initialize(object);
49 // Initialize link structure.
50 next_ = NULL;
51 }
52
~Node()53 ~Node() {
54 if (state_ != DESTROYED) Destroy();
55 #ifdef DEBUG
56 // Zap the values for eager trapping.
57 object_ = NULL;
58 next_ = NULL;
59 parameter_or_next_free_.next_free = NULL;
60 #endif
61 }
62
Destroy()63 void Destroy() {
64 if (state_ == WEAK || IsNearDeath()) {
65 GlobalHandles::number_of_weak_handles_--;
66 if (object_->IsJSGlobalObject()) {
67 GlobalHandles::number_of_global_object_weak_handles_--;
68 }
69 }
70 state_ = DESTROYED;
71 }
72
73 // Accessors for next_.
next()74 Node* next() { return next_; }
set_next(Node * value)75 void set_next(Node* value) { next_ = value; }
next_addr()76 Node** next_addr() { return &next_; }
77
78 // Accessors for next free node in the free list.
next_free()79 Node* next_free() {
80 ASSERT(state_ == DESTROYED);
81 return parameter_or_next_free_.next_free;
82 }
set_next_free(Node * value)83 void set_next_free(Node* value) {
84 ASSERT(state_ == DESTROYED);
85 parameter_or_next_free_.next_free = value;
86 }
87
88 // Returns a link from the handle.
FromLocation(Object ** location)89 static Node* FromLocation(Object** location) {
90 ASSERT(OFFSET_OF(Node, object_) == 0);
91 return reinterpret_cast<Node*>(location);
92 }
93
94 // Returns the handle.
handle()95 Handle<Object> handle() { return Handle<Object>(&object_); }
96
97 // Make this handle weak.
MakeWeak(void * parameter,WeakReferenceCallback callback)98 void MakeWeak(void* parameter, WeakReferenceCallback callback) {
99 LOG(HandleEvent("GlobalHandle::MakeWeak", handle().location()));
100 ASSERT(state_ != DESTROYED);
101 if (state_ != WEAK && !IsNearDeath()) {
102 GlobalHandles::number_of_weak_handles_++;
103 if (object_->IsJSGlobalObject()) {
104 GlobalHandles::number_of_global_object_weak_handles_++;
105 }
106 }
107 state_ = WEAK;
108 set_parameter(parameter);
109 callback_ = callback;
110 }
111
ClearWeakness()112 void ClearWeakness() {
113 LOG(HandleEvent("GlobalHandle::ClearWeakness", handle().location()));
114 ASSERT(state_ != DESTROYED);
115 if (state_ == WEAK || IsNearDeath()) {
116 GlobalHandles::number_of_weak_handles_--;
117 if (object_->IsJSGlobalObject()) {
118 GlobalHandles::number_of_global_object_weak_handles_--;
119 }
120 }
121 state_ = NORMAL;
122 set_parameter(NULL);
123 }
124
IsNearDeath()125 bool IsNearDeath() {
126 // Check for PENDING to ensure correct answer when processing callbacks.
127 return state_ == PENDING || state_ == NEAR_DEATH;
128 }
129
IsWeak()130 bool IsWeak() {
131 return state_ == WEAK;
132 }
133
134 // Returns the id for this weak handle.
set_parameter(void * parameter)135 void set_parameter(void* parameter) {
136 ASSERT(state_ != DESTROYED);
137 parameter_or_next_free_.parameter = parameter;
138 }
parameter()139 void* parameter() {
140 ASSERT(state_ != DESTROYED);
141 return parameter_or_next_free_.parameter;
142 }
143
144 // Returns the callback for this weak handle.
callback()145 WeakReferenceCallback callback() { return callback_; }
146
PostGarbageCollectionProcessing()147 bool PostGarbageCollectionProcessing() {
148 if (state_ != Node::PENDING) return false;
149 LOG(HandleEvent("GlobalHandle::Processing", handle().location()));
150 void* par = parameter();
151 state_ = NEAR_DEATH;
152 set_parameter(NULL);
153 // The callback function is resolved as late as possible to preserve old
154 // behavior.
155 WeakReferenceCallback func = callback();
156 if (func == NULL) return false;
157
158 v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle());
159 {
160 // Forbid reuse of destroyed nodes as they might be already deallocated.
161 // It's fine though to reuse nodes that were destroyed in weak callback
162 // as those cannot be deallocated until we are back from the callback.
163 set_first_free(NULL);
164 // Leaving V8.
165 VMState state(EXTERNAL);
166 func(object, par);
167 }
168 return true;
169 }
170
171 // Place the handle address first to avoid offset computation.
172 Object* object_; // Storage for object pointer.
173
174 // Transition diagram:
175 // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
176 enum State {
177 NORMAL, // Normal global handle.
178 WEAK, // Flagged as weak but not yet finalized.
179 PENDING, // Has been recognized as only reachable by weak handles.
180 NEAR_DEATH, // Callback has informed the handle is near death.
181 DESTROYED
182 };
183 State state_;
184
185 private:
186 // Handle specific callback.
187 WeakReferenceCallback callback_;
188 // Provided data for callback. In DESTROYED state, this is used for
189 // the free list link.
190 union {
191 void* parameter;
192 Node* next_free;
193 } parameter_or_next_free_;
194
195 // Linkage for the list.
196 Node* next_;
197
198 public:
199 TRACK_MEMORY("GlobalHandles::Node")
200 };
201
202
Create(Object * value)203 Handle<Object> GlobalHandles::Create(Object* value) {
204 Counters::global_handles.Increment();
205 Node* result;
206 if (first_free() == NULL) {
207 // Allocate a new node.
208 result = new Node(value);
209 result->set_next(head());
210 set_head(result);
211 } else {
212 // Take the first node in the free list.
213 result = first_free();
214 set_first_free(result->next_free());
215 result->Initialize(value);
216 }
217 return result->handle();
218 }
219
220
Destroy(Object ** location)221 void GlobalHandles::Destroy(Object** location) {
222 Counters::global_handles.Decrement();
223 if (location == NULL) return;
224 Node* node = Node::FromLocation(location);
225 node->Destroy();
226 // Link the destroyed.
227 node->set_next_free(first_free());
228 set_first_free(node);
229 }
230
231
MakeWeak(Object ** location,void * parameter,WeakReferenceCallback callback)232 void GlobalHandles::MakeWeak(Object** location, void* parameter,
233 WeakReferenceCallback callback) {
234 ASSERT(callback != NULL);
235 Node::FromLocation(location)->MakeWeak(parameter, callback);
236 }
237
238
ClearWeakness(Object ** location)239 void GlobalHandles::ClearWeakness(Object** location) {
240 Node::FromLocation(location)->ClearWeakness();
241 }
242
243
IsNearDeath(Object ** location)244 bool GlobalHandles::IsNearDeath(Object** location) {
245 return Node::FromLocation(location)->IsNearDeath();
246 }
247
248
IsWeak(Object ** location)249 bool GlobalHandles::IsWeak(Object** location) {
250 return Node::FromLocation(location)->IsWeak();
251 }
252
253
IterateWeakRoots(ObjectVisitor * v)254 void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
255 // Traversal of GC roots in the global handle list that are marked as
256 // WEAK or PENDING.
257 for (Node* current = head_; current != NULL; current = current->next()) {
258 if (current->state_ == Node::WEAK
259 || current->state_ == Node::PENDING
260 || current->state_ == Node::NEAR_DEATH) {
261 v->VisitPointer(¤t->object_);
262 }
263 }
264 }
265
266
IdentifyWeakHandles(WeakSlotCallback f)267 void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
268 for (Node* current = head_; current != NULL; current = current->next()) {
269 if (current->state_ == Node::WEAK) {
270 if (f(¤t->object_)) {
271 current->state_ = Node::PENDING;
272 LOG(HandleEvent("GlobalHandle::Pending", current->handle().location()));
273 }
274 }
275 }
276 }
277
278
279 int post_gc_processing_count = 0;
280
PostGarbageCollectionProcessing()281 void GlobalHandles::PostGarbageCollectionProcessing() {
282 // Process weak global handle callbacks. This must be done after the
283 // GC is completely done, because the callbacks may invoke arbitrary
284 // API functions.
285 // At the same time deallocate all DESTROYED nodes
286 ASSERT(Heap::gc_state() == Heap::NOT_IN_GC);
287 const int initial_post_gc_processing_count = ++post_gc_processing_count;
288 Node** p = &head_;
289 while (*p != NULL) {
290 if ((*p)->PostGarbageCollectionProcessing()) {
291 if (initial_post_gc_processing_count != post_gc_processing_count) {
292 // Weak callback triggered another GC and another round of
293 // PostGarbageCollection processing. The current node might
294 // have been deleted in that round, so we need to bail out (or
295 // restart the processing).
296 break;
297 }
298 }
299 if ((*p)->state_ == Node::DESTROYED) {
300 // Delete the link.
301 Node* node = *p;
302 *p = node->next(); // Update the link.
303 delete node;
304 } else {
305 p = (*p)->next_addr();
306 }
307 }
308 set_first_free(NULL);
309 }
310
311
IterateRoots(ObjectVisitor * v)312 void GlobalHandles::IterateRoots(ObjectVisitor* v) {
313 // Traversal of global handles marked as NORMAL or NEAR_DEATH.
314 for (Node* current = head_; current != NULL; current = current->next()) {
315 if (current->state_ == Node::NORMAL) {
316 v->VisitPointer(¤t->object_);
317 }
318 }
319 }
320
TearDown()321 void GlobalHandles::TearDown() {
322 // Delete all the nodes in the linked list.
323 Node* current = head_;
324 while (current != NULL) {
325 Node* n = current;
326 current = current->next();
327 delete n;
328 }
329 // Reset the head and free_list.
330 set_head(NULL);
331 set_first_free(NULL);
332 }
333
334
335 int GlobalHandles::number_of_weak_handles_ = 0;
336 int GlobalHandles::number_of_global_object_weak_handles_ = 0;
337
338 GlobalHandles::Node* GlobalHandles::head_ = NULL;
339 GlobalHandles::Node* GlobalHandles::first_free_ = NULL;
340
341 #ifdef DEBUG
342
PrintStats()343 void GlobalHandles::PrintStats() {
344 int total = 0;
345 int weak = 0;
346 int pending = 0;
347 int near_death = 0;
348 int destroyed = 0;
349
350 for (Node* current = head_; current != NULL; current = current->next()) {
351 total++;
352 if (current->state_ == Node::WEAK) weak++;
353 if (current->state_ == Node::PENDING) pending++;
354 if (current->state_ == Node::NEAR_DEATH) near_death++;
355 if (current->state_ == Node::DESTROYED) destroyed++;
356 }
357
358 PrintF("Global Handle Statistics:\n");
359 PrintF(" allocated memory = %dB\n", sizeof(Node) * total);
360 PrintF(" # weak = %d\n", weak);
361 PrintF(" # pending = %d\n", pending);
362 PrintF(" # near_death = %d\n", near_death);
363 PrintF(" # destroyed = %d\n", destroyed);
364 PrintF(" # total = %d\n", total);
365 }
366
Print()367 void GlobalHandles::Print() {
368 PrintF("Global handles:\n");
369 for (Node* current = head_; current != NULL; current = current->next()) {
370 PrintF(" handle %p to %p (weak=%d)\n", current->handle().location(),
371 *current->handle(), current->state_ == Node::WEAK);
372 }
373 }
374
375 #endif
376
ObjectGroups()377 List<ObjectGroup*>* GlobalHandles::ObjectGroups() {
378 // Lazily initialize the list to avoid startup time static constructors.
379 static List<ObjectGroup*> groups(4);
380 return &groups;
381 }
382
AddGroup(Object *** handles,size_t length)383 void GlobalHandles::AddGroup(Object*** handles, size_t length) {
384 ObjectGroup* new_entry = new ObjectGroup(length);
385 for (size_t i = 0; i < length; ++i)
386 new_entry->objects_.Add(handles[i]);
387 ObjectGroups()->Add(new_entry);
388 }
389
390
RemoveObjectGroups()391 void GlobalHandles::RemoveObjectGroups() {
392 List<ObjectGroup*>* object_groups = ObjectGroups();
393 for (int i = 0; i< object_groups->length(); i++) {
394 delete object_groups->at(i);
395 }
396 object_groups->Clear();
397 }
398
399
400 } } // namespace v8::internal
401