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