Allocate global handles in chunks.
authorantonm@chromium.org <antonm@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 26 Oct 2009 12:54:41 +0000 (12:54 +0000)
committerantonm@chromium.org <antonm@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 26 Oct 2009 12:54:41 +0000 (12:54 +0000)
Review URL: http://codereview.chromium.org/327008

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3128 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/global-handles.cc
src/global-handles.h

index f4b69fcdd3038b6c1ef322eac24612a1964cb7d3..c6cc288e6abd33289c10c36a4e1ad3c94eb6bbd0 100644 (file)
@@ -44,6 +44,10 @@ class GlobalHandles::Node : public Malloced {
     callback_ = NULL;
   }
 
+  Node() {
+    state_ = DESTROYED;
+  }
+
   explicit Node(Object* object) {
     Initialize(object);
     // Initialize link structure.
@@ -200,20 +204,80 @@ class GlobalHandles::Node : public Malloced {
 };
 
 
+class GlobalHandles::Pool BASE_EMBEDDED {
+  public:
+    Pool() {
+      current_ = new Chunk();
+      current_->previous = NULL;
+      next_ = current_->nodes;
+      limit_ = current_->nodes + kNodesPerChunk;
+    }
+
+    Node* Allocate() {
+      if (next_ < limit_) {
+        return next_++;
+      }
+      return SlowAllocate();
+    }
+
+    void Release() {
+      Chunk* current = current_;
+      ASSERT(current != NULL);  // At least a single block must by allocated
+      do {
+        Chunk* previous = current->previous;
+        delete current;
+        current = previous;
+      } while (current != NULL);
+      current_ = NULL;
+      next_ = limit_ = NULL;
+    }
+
+  private:
+    static const int kNodesPerChunk = (1 << 12) - 1;
+    struct Chunk : public Malloced {
+      Chunk* previous;
+      Node nodes[kNodesPerChunk];
+    };
+
+    Node* SlowAllocate() {
+      Chunk* chunk = new Chunk();
+      chunk->previous = current_;
+      current_ = chunk;
+
+      Node* new_nodes = current_->nodes;
+      next_ = new_nodes + 1;
+      limit_ = new_nodes + kNodesPerChunk;
+      return new_nodes;
+    }
+
+    Chunk* current_;
+    Node* next_;
+    Node* limit_;
+};
+
+
+static GlobalHandles::Pool pool_;
+
+
 Handle<Object> GlobalHandles::Create(Object* value) {
   Counters::global_handles.Increment();
   Node* result;
-  if (first_free() == NULL) {
-    // Allocate a new node.
-    result = new Node(value);
-    result->set_next(head());
-    set_head(result);
-  } else {
+  if (first_free()) {
     // Take the first node in the free list.
     result = first_free();
     set_first_free(result->next_free());
-    result->Initialize(value);
+  } else if (first_deallocated()) {
+    // Next try deallocated list
+    result = first_deallocated();
+    set_first_deallocated(result->next_free());
+    set_head(result);
+  } else {
+    // Allocate a new node.
+    result = pool_.Allocate();
+    result->set_next(head());
+    set_head(result);
   }
+  result->Initialize(value);
   return result->handle();
 }
 
@@ -292,7 +356,7 @@ void GlobalHandles::PostGarbageCollectionProcessing() {
   // Process weak global handle callbacks. This must be done after the
   // GC is completely done, because the callbacks may invoke arbitrary
   // API functions.
-  // At the same time deallocate all DESTROYED nodes
+  // At the same time deallocate all DESTROYED nodes.
   ASSERT(Heap::gc_state() == Heap::NOT_IN_GC);
   const int initial_post_gc_processing_count = ++post_gc_processing_count;
   Node** p = &head_;
@@ -310,12 +374,19 @@ void GlobalHandles::PostGarbageCollectionProcessing() {
       // Delete the link.
       Node* node = *p;
       *p = node->next();  // Update the link.
-      delete node;
+      if (first_deallocated()) {
+        first_deallocated()->set_next(node);
+      }
+      node->set_next_free(first_deallocated());
+      set_first_deallocated(node);
     } else {
       p = (*p)->next_addr();
     }
   }
   set_first_free(NULL);
+  if (first_deallocated()) {
+    first_deallocated()->set_next(head());
+  }
 }
 
 
@@ -329,16 +400,11 @@ void GlobalHandles::IterateRoots(ObjectVisitor* v) {
 }
 
 void GlobalHandles::TearDown() {
-  // Delete all the nodes in the linked list.
-  Node* current = head_;
-  while (current != NULL) {
-    Node* n = current;
-    current = current->next();
-    delete n;
-  }
-  // Reset the head and free_list.
+  // Reset all the lists.
   set_head(NULL);
   set_first_free(NULL);
+  set_first_deallocated(NULL);
+  pool_.Release();
 }
 
 
@@ -347,6 +413,7 @@ int GlobalHandles::number_of_global_object_weak_handles_ = 0;
 
 GlobalHandles::Node* GlobalHandles::head_ = NULL;
 GlobalHandles::Node* GlobalHandles::first_free_ = NULL;
+GlobalHandles::Node* GlobalHandles::first_deallocated_ = NULL;
 
 #ifdef DEBUG
 
index feb95bf2a3dfe7d73ecd4d2e1e401d07c22e3e90..87eb9b83017110a2dec434f90ee6f33f4263586f 100644 (file)
@@ -127,6 +127,7 @@ class GlobalHandles : public AllStatic {
   static void PrintStats();
   static void Print();
 #endif
+  class Pool;
  private:
   // Internal node structure, one for each global handle.
   class Node;
@@ -148,6 +149,23 @@ class GlobalHandles : public AllStatic {
   static Node* first_free_;
   static Node* first_free() { return first_free_; }
   static void set_first_free(Node* value) { first_free_ = value; }
+
+  // List of deallocated nodes.
+  // Deallocated nodes form a prefix of all the nodes and
+  // |first_deallocated| points to last deallocated node before
+  // |head|.  Those deallocated nodes are additionally linked
+  // by |next_free|:
+  //                                    1st deallocated  head
+  //                                           |          |
+  //                                           V          V
+  //    node          node        ...         node       node
+  //      .next      -> .next ->                .next ->
+  //   <- .next_free <- .next_free           <- .next_free
+  static Node* first_deallocated_;
+  static Node* first_deallocated() { return first_deallocated_; }
+  static void set_first_deallocated(Node* value) {
+    first_deallocated_ = value;
+  }
 };