• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "vm-state-inl.h"
34 
35 namespace v8 {
36 namespace internal {
37 
38 
~ObjectGroup()39 ObjectGroup::~ObjectGroup() {
40   if (info_ != NULL) info_->Dispose();
41 }
42 
43 
44 class GlobalHandles::Node : public Malloced {
45  public:
46 
Initialize(Object * object)47   void Initialize(Object* object) {
48     // Set the initial value of the handle.
49     object_ = object;
50     class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
51     state_  = NORMAL;
52     parameter_or_next_free_.parameter = NULL;
53     callback_ = NULL;
54   }
55 
Node()56   Node() {
57     state_ = DESTROYED;
58   }
59 
Node(Object * object)60   explicit Node(Object* object) {
61     Initialize(object);
62     // Initialize link structure.
63     next_ = NULL;
64   }
65 
~Node()66   ~Node() {
67     if (state_ != DESTROYED) Destroy(Isolate::Current()->global_handles());
68 #ifdef DEBUG
69     // Zap the values for eager trapping.
70     object_ = NULL;
71     next_ = NULL;
72     parameter_or_next_free_.next_free = NULL;
73 #endif
74   }
75 
Destroy(GlobalHandles * global_handles)76   void Destroy(GlobalHandles* global_handles) {
77     if (state_ == WEAK || IsNearDeath()) {
78       global_handles->number_of_weak_handles_--;
79       if (object_->IsJSGlobalObject()) {
80         global_handles->number_of_global_object_weak_handles_--;
81       }
82     }
83     state_ = DESTROYED;
84   }
85 
86   // Accessors for next_.
next()87   Node* next() { return next_; }
set_next(Node * value)88   void set_next(Node* value) { next_ = value; }
next_addr()89   Node** next_addr() { return &next_; }
90 
91   // Accessors for next free node in the free list.
next_free()92   Node* next_free() {
93     ASSERT(state_ == DESTROYED);
94     return parameter_or_next_free_.next_free;
95   }
set_next_free(Node * value)96   void set_next_free(Node* value) {
97     ASSERT(state_ == DESTROYED);
98     parameter_or_next_free_.next_free = value;
99   }
100 
101   // Returns a link from the handle.
FromLocation(Object ** location)102   static Node* FromLocation(Object** location) {
103     ASSERT(OFFSET_OF(Node, object_) == 0);
104     return reinterpret_cast<Node*>(location);
105   }
106 
107   // Returns the handle.
handle()108   Handle<Object> handle() { return Handle<Object>(&object_); }
109 
110   // Make this handle weak.
MakeWeak(GlobalHandles * global_handles,void * parameter,WeakReferenceCallback callback)111   void MakeWeak(GlobalHandles* global_handles, void* parameter,
112                 WeakReferenceCallback callback) {
113     LOG(global_handles->isolate(),
114         HandleEvent("GlobalHandle::MakeWeak", handle().location()));
115     ASSERT(state_ != DESTROYED);
116     if (state_ != WEAK && !IsNearDeath()) {
117       global_handles->number_of_weak_handles_++;
118       if (object_->IsJSGlobalObject()) {
119         global_handles->number_of_global_object_weak_handles_++;
120       }
121     }
122     state_ = WEAK;
123     set_parameter(parameter);
124     callback_ = callback;
125   }
126 
ClearWeakness(GlobalHandles * global_handles)127   void ClearWeakness(GlobalHandles* global_handles) {
128     LOG(global_handles->isolate(),
129         HandleEvent("GlobalHandle::ClearWeakness", handle().location()));
130     ASSERT(state_ != DESTROYED);
131     if (state_ == WEAK || IsNearDeath()) {
132       global_handles->number_of_weak_handles_--;
133       if (object_->IsJSGlobalObject()) {
134         global_handles->number_of_global_object_weak_handles_--;
135       }
136     }
137     state_ = NORMAL;
138     set_parameter(NULL);
139   }
140 
IsNearDeath()141   bool IsNearDeath() {
142     // Check for PENDING to ensure correct answer when processing callbacks.
143     return state_ == PENDING || state_ == NEAR_DEATH;
144   }
145 
IsWeak()146   bool IsWeak() {
147     return state_ == WEAK;
148   }
149 
CanBeRetainer()150   bool CanBeRetainer() {
151     return state_ != DESTROYED && state_ != NEAR_DEATH;
152   }
153 
SetWrapperClassId(uint16_t class_id)154   void SetWrapperClassId(uint16_t class_id) {
155     class_id_ = class_id;
156   }
157 
158   // Returns the id for this weak handle.
set_parameter(void * parameter)159   void set_parameter(void* parameter) {
160     ASSERT(state_ != DESTROYED);
161     parameter_or_next_free_.parameter = parameter;
162   }
parameter()163   void* parameter() {
164     ASSERT(state_ != DESTROYED);
165     return parameter_or_next_free_.parameter;
166   }
167 
168   // Returns the callback for this weak handle.
callback()169   WeakReferenceCallback callback() { return callback_; }
170 
PostGarbageCollectionProcessing(Isolate * isolate,GlobalHandles * global_handles)171   bool PostGarbageCollectionProcessing(Isolate* isolate,
172                                        GlobalHandles* global_handles) {
173     if (state_ != Node::PENDING) return false;
174     LOG(isolate, HandleEvent("GlobalHandle::Processing", handle().location()));
175     WeakReferenceCallback func = callback();
176     if (func == NULL) {
177       Destroy(global_handles);
178       return false;
179     }
180     void* par = parameter();
181     state_ = NEAR_DEATH;
182     set_parameter(NULL);
183 
184     v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle());
185     {
186       // Forbid reuse of destroyed nodes as they might be already deallocated.
187       // It's fine though to reuse nodes that were destroyed in weak callback
188       // as those cannot be deallocated until we are back from the callback.
189       global_handles->set_first_free(NULL);
190       if (global_handles->first_deallocated()) {
191         global_handles->first_deallocated()->set_next(global_handles->head());
192       }
193       // Check that we are not passing a finalized external string to
194       // the callback.
195       ASSERT(!object_->IsExternalAsciiString() ||
196              ExternalAsciiString::cast(object_)->resource() != NULL);
197       ASSERT(!object_->IsExternalTwoByteString() ||
198              ExternalTwoByteString::cast(object_)->resource() != NULL);
199       // Leaving V8.
200       VMState state(isolate, EXTERNAL);
201       func(object, par);
202     }
203     // Absense of explicit cleanup or revival of weak handle
204     // in most of the cases would lead to memory leak.
205     ASSERT(state_ != NEAR_DEATH);
206     return true;
207   }
208 
209   // Place the handle address first to avoid offset computation.
210   Object* object_;  // Storage for object pointer.
211 
212   uint16_t class_id_;
213 
214   // Transition diagram:
215   // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
216   enum State {
217     NORMAL,      // Normal global handle.
218     WEAK,        // Flagged as weak but not yet finalized.
219     PENDING,     // Has been recognized as only reachable by weak handles.
220     NEAR_DEATH,  // Callback has informed the handle is near death.
221     DESTROYED
222   };
223   State state_ : 4;  // Need one more bit for MSVC as it treats enums as signed.
224 
225  private:
226   // Handle specific callback.
227   WeakReferenceCallback callback_;
228   // Provided data for callback.  In DESTROYED state, this is used for
229   // the free list link.
230   union {
231     void* parameter;
232     Node* next_free;
233   } parameter_or_next_free_;
234 
235   // Linkage for the list.
236   Node* next_;
237 
238  public:
239   TRACK_MEMORY("GlobalHandles::Node")
240 };
241 
242 
243 class GlobalHandles::Pool {
244   public:
Pool()245     Pool() {
246       current_ = new Chunk();
247       current_->previous = NULL;
248       next_ = current_->nodes;
249       limit_ = current_->nodes + kNodesPerChunk;
250     }
251 
~Pool()252     ~Pool() {
253       if (current_ != NULL) {
254         Release();
255       }
256     }
257 
Allocate()258     Node* Allocate() {
259       if (next_ < limit_) {
260         return next_++;
261       }
262       return SlowAllocate();
263     }
264 
Release()265     void Release() {
266       Chunk* current = current_;
267       ASSERT(current != NULL);  // At least a single block must by allocated
268       do {
269         Chunk* previous = current->previous;
270         delete current;
271         current = previous;
272       } while (current != NULL);
273       current_ = NULL;
274       next_ = limit_ = NULL;
275     }
276 
277   private:
278     static const int kNodesPerChunk = (1 << 12) - 1;
279     struct Chunk : public Malloced {
280       Chunk* previous;
281       Node nodes[kNodesPerChunk];
282     };
283 
SlowAllocate()284     Node* SlowAllocate() {
285       Chunk* chunk = new Chunk();
286       chunk->previous = current_;
287       current_ = chunk;
288 
289       Node* new_nodes = current_->nodes;
290       next_ = new_nodes + 1;
291       limit_ = new_nodes + kNodesPerChunk;
292       return new_nodes;
293     }
294 
295     Chunk* current_;
296     Node* next_;
297     Node* limit_;
298 };
299 
300 
GlobalHandles(Isolate * isolate)301 GlobalHandles::GlobalHandles(Isolate* isolate)
302     : isolate_(isolate),
303       number_of_weak_handles_(0),
304       number_of_global_object_weak_handles_(0),
305       head_(NULL),
306       first_free_(NULL),
307       first_deallocated_(NULL),
308       pool_(new Pool()),
309       post_gc_processing_count_(0),
310       object_groups_(4) {
311 }
312 
313 
~GlobalHandles()314 GlobalHandles::~GlobalHandles() {
315   delete pool_;
316   pool_ = 0;
317 }
318 
319 
Create(Object * value)320 Handle<Object> GlobalHandles::Create(Object* value) {
321   isolate_->counters()->global_handles()->Increment();
322   Node* result;
323   if (first_free()) {
324     // Take the first node in the free list.
325     result = first_free();
326     set_first_free(result->next_free());
327   } else if (first_deallocated()) {
328     // Next try deallocated list
329     result = first_deallocated();
330     set_first_deallocated(result->next_free());
331     ASSERT(result->next() == head());
332     set_head(result);
333   } else {
334     // Allocate a new node.
335     result = pool_->Allocate();
336     result->set_next(head());
337     set_head(result);
338   }
339   result->Initialize(value);
340   return result->handle();
341 }
342 
343 
Destroy(Object ** location)344 void GlobalHandles::Destroy(Object** location) {
345   isolate_->counters()->global_handles()->Decrement();
346   if (location == NULL) return;
347   Node* node = Node::FromLocation(location);
348   node->Destroy(this);
349   // Link the destroyed.
350   node->set_next_free(first_free());
351   set_first_free(node);
352 }
353 
354 
MakeWeak(Object ** location,void * parameter,WeakReferenceCallback callback)355 void GlobalHandles::MakeWeak(Object** location, void* parameter,
356                              WeakReferenceCallback callback) {
357   ASSERT(callback != NULL);
358   Node::FromLocation(location)->MakeWeak(this, parameter, callback);
359 }
360 
361 
ClearWeakness(Object ** location)362 void GlobalHandles::ClearWeakness(Object** location) {
363   Node::FromLocation(location)->ClearWeakness(this);
364 }
365 
366 
IsNearDeath(Object ** location)367 bool GlobalHandles::IsNearDeath(Object** location) {
368   return Node::FromLocation(location)->IsNearDeath();
369 }
370 
371 
IsWeak(Object ** location)372 bool GlobalHandles::IsWeak(Object** location) {
373   return Node::FromLocation(location)->IsWeak();
374 }
375 
376 
SetWrapperClassId(Object ** location,uint16_t class_id)377 void GlobalHandles::SetWrapperClassId(Object** location, uint16_t class_id) {
378   Node::FromLocation(location)->SetWrapperClassId(class_id);
379 }
380 
381 
IterateWeakRoots(ObjectVisitor * v)382 void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
383   // Traversal of GC roots in the global handle list that are marked as
384   // WEAK or PENDING.
385   for (Node* current = head_; current != NULL; current = current->next()) {
386     if (current->state_ == Node::WEAK
387       || current->state_ == Node::PENDING
388       || current->state_ == Node::NEAR_DEATH) {
389       v->VisitPointer(&current->object_);
390     }
391   }
392 }
393 
394 
IterateWeakRoots(WeakReferenceGuest f,WeakReferenceCallback callback)395 void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f,
396                                      WeakReferenceCallback callback) {
397   for (Node* current = head_; current != NULL; current = current->next()) {
398     if (current->IsWeak() && current->callback() == callback) {
399       f(current->object_, current->parameter());
400     }
401   }
402 }
403 
404 
IdentifyWeakHandles(WeakSlotCallback f)405 void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
406   for (Node* current = head_; current != NULL; current = current->next()) {
407     if (current->state_ == Node::WEAK) {
408       if (f(&current->object_)) {
409         current->state_ = Node::PENDING;
410         LOG(isolate_,
411             HandleEvent("GlobalHandle::Pending", current->handle().location()));
412       }
413     }
414   }
415 }
416 
417 
PostGarbageCollectionProcessing()418 bool GlobalHandles::PostGarbageCollectionProcessing() {
419   // Process weak global handle callbacks. This must be done after the
420   // GC is completely done, because the callbacks may invoke arbitrary
421   // API functions.
422   // At the same time deallocate all DESTROYED nodes.
423   ASSERT(isolate_->heap()->gc_state() == Heap::NOT_IN_GC);
424   const int initial_post_gc_processing_count = ++post_gc_processing_count_;
425   bool next_gc_likely_to_collect_more = false;
426   Node** p = &head_;
427   while (*p != NULL) {
428     if ((*p)->PostGarbageCollectionProcessing(isolate_, this)) {
429       if (initial_post_gc_processing_count != post_gc_processing_count_) {
430         // Weak callback triggered another GC and another round of
431         // PostGarbageCollection processing.  The current node might
432         // have been deleted in that round, so we need to bail out (or
433         // restart the processing).
434         break;
435       }
436     }
437     if ((*p)->state_ == Node::DESTROYED) {
438       // Delete the link.
439       Node* node = *p;
440       *p = node->next();  // Update the link.
441       if (first_deallocated()) {
442         first_deallocated()->set_next(node);
443       }
444       node->set_next_free(first_deallocated());
445       set_first_deallocated(node);
446       next_gc_likely_to_collect_more = true;
447     } else {
448       p = (*p)->next_addr();
449     }
450   }
451   set_first_free(NULL);
452   if (first_deallocated()) {
453     first_deallocated()->set_next(head());
454   }
455 
456   return next_gc_likely_to_collect_more;
457 }
458 
459 
IterateStrongRoots(ObjectVisitor * v)460 void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) {
461   // Traversal of global handles marked as NORMAL.
462   for (Node* current = head_; current != NULL; current = current->next()) {
463     if (current->state_ == Node::NORMAL) {
464       v->VisitPointer(&current->object_);
465     }
466   }
467 }
468 
469 
IterateAllRoots(ObjectVisitor * v)470 void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
471   for (Node* current = head_; current != NULL; current = current->next()) {
472     if (current->state_ != Node::DESTROYED) {
473       v->VisitPointer(&current->object_);
474     }
475   }
476 }
477 
478 
IterateAllRootsWithClassIds(ObjectVisitor * v)479 void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) {
480   for (Node* current = head_; current != NULL; current = current->next()) {
481     if (current->class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId &&
482         current->CanBeRetainer()) {
483       v->VisitEmbedderReference(&current->object_, current->class_id_);
484     }
485   }
486 }
487 
488 
TearDown()489 void GlobalHandles::TearDown() {
490   // Reset all the lists.
491   set_head(NULL);
492   set_first_free(NULL);
493   set_first_deallocated(NULL);
494   pool_->Release();
495 }
496 
497 
RecordStats(HeapStats * stats)498 void GlobalHandles::RecordStats(HeapStats* stats) {
499   *stats->global_handle_count = 0;
500   *stats->weak_global_handle_count = 0;
501   *stats->pending_global_handle_count = 0;
502   *stats->near_death_global_handle_count = 0;
503   *stats->destroyed_global_handle_count = 0;
504   for (Node* current = head_; current != NULL; current = current->next()) {
505     *stats->global_handle_count += 1;
506     if (current->state_ == Node::WEAK) {
507       *stats->weak_global_handle_count += 1;
508     } else if (current->state_ == Node::PENDING) {
509       *stats->pending_global_handle_count += 1;
510     } else if (current->state_ == Node::NEAR_DEATH) {
511       *stats->near_death_global_handle_count += 1;
512     } else if (current->state_ == Node::DESTROYED) {
513       *stats->destroyed_global_handle_count += 1;
514     }
515   }
516 }
517 
518 #ifdef DEBUG
519 
PrintStats()520 void GlobalHandles::PrintStats() {
521   int total = 0;
522   int weak = 0;
523   int pending = 0;
524   int near_death = 0;
525   int destroyed = 0;
526 
527   for (Node* current = head_; current != NULL; current = current->next()) {
528     total++;
529     if (current->state_ == Node::WEAK) weak++;
530     if (current->state_ == Node::PENDING) pending++;
531     if (current->state_ == Node::NEAR_DEATH) near_death++;
532     if (current->state_ == Node::DESTROYED) destroyed++;
533   }
534 
535   PrintF("Global Handle Statistics:\n");
536   PrintF("  allocated memory = %" V8_PTR_PREFIX "dB\n", sizeof(Node) * total);
537   PrintF("  # weak       = %d\n", weak);
538   PrintF("  # pending    = %d\n", pending);
539   PrintF("  # near_death = %d\n", near_death);
540   PrintF("  # destroyed  = %d\n", destroyed);
541   PrintF("  # total      = %d\n", total);
542 }
543 
Print()544 void GlobalHandles::Print() {
545   PrintF("Global handles:\n");
546   for (Node* current = head_; current != NULL; current = current->next()) {
547     PrintF("  handle %p to %p (weak=%d)\n",
548            reinterpret_cast<void*>(current->handle().location()),
549            reinterpret_cast<void*>(*current->handle()),
550            current->state_ == Node::WEAK);
551   }
552 }
553 
554 #endif
555 
556 
557 
AddObjectGroup(Object *** handles,size_t length,v8::RetainedObjectInfo * info)558 void GlobalHandles::AddObjectGroup(Object*** handles,
559                                    size_t length,
560                                    v8::RetainedObjectInfo* info) {
561   if (length == 0) {
562     if (info != NULL) info->Dispose();
563     return;
564   }
565   object_groups_.Add(ObjectGroup::New(handles, length, info));
566 }
567 
568 
AddImplicitReferences(HeapObject ** parent,Object *** children,size_t length)569 void GlobalHandles::AddImplicitReferences(HeapObject** parent,
570                                           Object*** children,
571                                           size_t length) {
572   if (length == 0) return;
573   implicit_ref_groups_.Add(ImplicitRefGroup::New(parent, children, length));
574 }
575 
576 
RemoveObjectGroups()577 void GlobalHandles::RemoveObjectGroups() {
578   for (int i = 0; i < object_groups_.length(); i++) {
579     object_groups_.at(i)->Dispose();
580   }
581   object_groups_.Clear();
582 }
583 
584 
RemoveImplicitRefGroups()585 void GlobalHandles::RemoveImplicitRefGroups() {
586   for (int i = 0; i < implicit_ref_groups_.length(); i++) {
587     implicit_ref_groups_.at(i)->Dispose();
588   }
589   implicit_ref_groups_.Clear();
590 }
591 
592 
593 } }  // namespace v8::internal
594