Home | History | Annotate | Download | only in src
      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 
     39 ObjectGroup::~ObjectGroup() {
     40   if (info_ != NULL) info_->Dispose();
     41 }
     42 
     43 
     44 class GlobalHandles::Node : public Malloced {
     45  public:
     46 
     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 
     56   Node() {
     57     state_ = DESTROYED;
     58   }
     59 
     60   explicit Node(Object* object) {
     61     Initialize(object);
     62     // Initialize link structure.
     63     next_ = NULL;
     64   }
     65 
     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 
     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_.
     87   Node* next() { return next_; }
     88   void set_next(Node* value) { next_ = value; }
     89   Node** next_addr() { return &next_; }
     90 
     91   // Accessors for next free node in the free list.
     92   Node* next_free() {
     93     ASSERT(state_ == DESTROYED);
     94     return parameter_or_next_free_.next_free;
     95   }
     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.
    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.
    108   Handle<Object> handle() { return Handle<Object>(&object_); }
    109 
    110   // Make this handle weak.
    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 
    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 
    141   bool IsNearDeath() {
    142     // Check for PENDING to ensure correct answer when processing callbacks.
    143     return state_ == PENDING || state_ == NEAR_DEATH;
    144   }
    145 
    146   bool IsWeak() {
    147     return state_ == WEAK;
    148   }
    149 
    150   bool CanBeRetainer() {
    151     return state_ != DESTROYED && state_ != NEAR_DEATH;
    152   }
    153 
    154   void SetWrapperClassId(uint16_t class_id) {
    155     class_id_ = class_id;
    156   }
    157 
    158   // Returns the id for this weak handle.
    159   void set_parameter(void* parameter) {
    160     ASSERT(state_ != DESTROYED);
    161     parameter_or_next_free_.parameter = parameter;
    162   }
    163   void* parameter() {
    164     ASSERT(state_ != DESTROYED);
    165     return parameter_or_next_free_.parameter;
    166   }
    167 
    168   // Returns the callback for this weak handle.
    169   WeakReferenceCallback callback() { return callback_; }
    170 
    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:
    245     Pool() {
    246       current_ = new Chunk();
    247       current_->previous = NULL;
    248       next_ = current_->nodes;
    249       limit_ = current_->nodes + kNodesPerChunk;
    250     }
    251 
    252     ~Pool() {
    253       if (current_ != NULL) {
    254         Release();
    255       }
    256     }
    257 
    258     Node* Allocate() {
    259       if (next_ < limit_) {
    260         return next_++;
    261       }
    262       return SlowAllocate();
    263     }
    264 
    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 
    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 
    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 
    314 GlobalHandles::~GlobalHandles() {
    315   delete pool_;
    316   pool_ = 0;
    317 }
    318 
    319 
    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 
    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 
    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 
    362 void GlobalHandles::ClearWeakness(Object** location) {
    363   Node::FromLocation(location)->ClearWeakness(this);
    364 }
    365 
    366 
    367 bool GlobalHandles::IsNearDeath(Object** location) {
    368   return Node::FromLocation(location)->IsNearDeath();
    369 }
    370 
    371 
    372 bool GlobalHandles::IsWeak(Object** location) {
    373   return Node::FromLocation(location)->IsWeak();
    374 }
    375 
    376 
    377 void GlobalHandles::SetWrapperClassId(Object** location, uint16_t class_id) {
    378   Node::FromLocation(location)->SetWrapperClassId(class_id);
    379 }
    380 
    381 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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