• 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 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()47   Node() {
48     state_ = DESTROYED;
49   }
50 
Node(Object * object)51   explicit Node(Object* object) {
52     Initialize(object);
53     // Initialize link structure.
54     next_ = NULL;
55   }
56 
~Node()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 
Destroy()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_.
next()78   Node* next() { return next_; }
set_next(Node * value)79   void set_next(Node* value) { next_ = value; }
next_addr()80   Node** next_addr() { return &next_; }
81 
82   // Accessors for next free node in the free list.
next_free()83   Node* next_free() {
84     ASSERT(state_ == DESTROYED);
85     return parameter_or_next_free_.next_free;
86   }
set_next_free(Node * value)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.
FromLocation(Object ** location)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.
handle()99   Handle<Object> handle() { return Handle<Object>(&object_); }
100 
101   // Make this handle weak.
MakeWeak(void * parameter,WeakReferenceCallback callback)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 
ClearWeakness()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 
IsNearDeath()129   bool IsNearDeath() {
130     // Check for PENDING to ensure correct answer when processing callbacks.
131     return state_ == PENDING || state_ == NEAR_DEATH;
132   }
133 
IsWeak()134   bool IsWeak() {
135     return state_ == WEAK;
136   }
137 
138   // Returns the id for this weak handle.
set_parameter(void * parameter)139   void set_parameter(void* parameter) {
140     ASSERT(state_ != DESTROYED);
141     parameter_or_next_free_.parameter = parameter;
142   }
parameter()143   void* parameter() {
144     ASSERT(state_ != DESTROYED);
145     return parameter_or_next_free_.parameter;
146   }
147 
148   // Returns the callback for this weak handle.
callback()149   WeakReferenceCallback callback() { return callback_; }
150 
PostGarbageCollectionProcessing()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:
Pool()218     Pool() {
219       current_ = new Chunk();
220       current_->previous = NULL;
221       next_ = current_->nodes;
222       limit_ = current_->nodes + kNodesPerChunk;
223     }
224 
Allocate()225     Node* Allocate() {
226       if (next_ < limit_) {
227         return next_++;
228       }
229       return SlowAllocate();
230     }
231 
Release()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 
SlowAllocate()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 
Create(Object * value)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 
Destroy(Object ** location)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 
MakeWeak(Object ** location,void * parameter,WeakReferenceCallback callback)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 
ClearWeakness(Object ** location)313 void GlobalHandles::ClearWeakness(Object** location) {
314   Node::FromLocation(location)->ClearWeakness();
315 }
316 
317 
IsNearDeath(Object ** location)318 bool GlobalHandles::IsNearDeath(Object** location) {
319   return Node::FromLocation(location)->IsNearDeath();
320 }
321 
322 
IsWeak(Object ** location)323 bool GlobalHandles::IsWeak(Object** location) {
324   return Node::FromLocation(location)->IsWeak();
325 }
326 
327 
IterateWeakRoots(ObjectVisitor * v)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 
IterateWeakRoots(WeakReferenceGuest f,WeakReferenceCallback callback)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 
IdentifyWeakHandles(WeakSlotCallback f)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 
PostGarbageCollectionProcessing()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 
IterateStrongRoots(ObjectVisitor * v)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 
IterateAllRoots(ObjectVisitor * v)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 
TearDown()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 
RecordStats(HeapStats * stats)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 
PrintStats()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 
Print()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 
ObjectGroups()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 
AddGroup(Object *** handles,size_t length)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 
RemoveObjectGroups()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