• 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(Object * object)47   explicit Node(Object* object) {
48     Initialize(object);
49     // Initialize link structure.
50     next_ = NULL;
51   }
52 
~Node()53   ~Node() {
54     if (state_ != DESTROYED) Destroy();
55 #ifdef DEBUG
56     // Zap the values for eager trapping.
57     object_ = NULL;
58     next_ = NULL;
59     parameter_or_next_free_.next_free = NULL;
60 #endif
61   }
62 
Destroy()63   void Destroy() {
64     if (state_ == WEAK || IsNearDeath()) {
65       GlobalHandles::number_of_weak_handles_--;
66       if (object_->IsJSGlobalObject()) {
67         GlobalHandles::number_of_global_object_weak_handles_--;
68       }
69     }
70     state_ = DESTROYED;
71   }
72 
73   // Accessors for next_.
next()74   Node* next() { return next_; }
set_next(Node * value)75   void set_next(Node* value) { next_ = value; }
next_addr()76   Node** next_addr() { return &next_; }
77 
78   // Accessors for next free node in the free list.
next_free()79   Node* next_free() {
80     ASSERT(state_ == DESTROYED);
81     return parameter_or_next_free_.next_free;
82   }
set_next_free(Node * value)83   void set_next_free(Node* value) {
84     ASSERT(state_ == DESTROYED);
85     parameter_or_next_free_.next_free = value;
86   }
87 
88   // Returns a link from the handle.
FromLocation(Object ** location)89   static Node* FromLocation(Object** location) {
90     ASSERT(OFFSET_OF(Node, object_) == 0);
91     return reinterpret_cast<Node*>(location);
92   }
93 
94   // Returns the handle.
handle()95   Handle<Object> handle() { return Handle<Object>(&object_); }
96 
97   // Make this handle weak.
MakeWeak(void * parameter,WeakReferenceCallback callback)98   void MakeWeak(void* parameter, WeakReferenceCallback callback) {
99     LOG(HandleEvent("GlobalHandle::MakeWeak", handle().location()));
100     ASSERT(state_ != DESTROYED);
101     if (state_ != WEAK && !IsNearDeath()) {
102       GlobalHandles::number_of_weak_handles_++;
103       if (object_->IsJSGlobalObject()) {
104         GlobalHandles::number_of_global_object_weak_handles_++;
105       }
106     }
107     state_ = WEAK;
108     set_parameter(parameter);
109     callback_ = callback;
110   }
111 
ClearWeakness()112   void ClearWeakness() {
113     LOG(HandleEvent("GlobalHandle::ClearWeakness", handle().location()));
114     ASSERT(state_ != DESTROYED);
115     if (state_ == WEAK || IsNearDeath()) {
116       GlobalHandles::number_of_weak_handles_--;
117       if (object_->IsJSGlobalObject()) {
118         GlobalHandles::number_of_global_object_weak_handles_--;
119       }
120     }
121     state_ = NORMAL;
122     set_parameter(NULL);
123   }
124 
IsNearDeath()125   bool IsNearDeath() {
126     // Check for PENDING to ensure correct answer when processing callbacks.
127     return state_ == PENDING || state_ == NEAR_DEATH;
128   }
129 
IsWeak()130   bool IsWeak() {
131     return state_ == WEAK;
132   }
133 
134   // Returns the id for this weak handle.
set_parameter(void * parameter)135   void set_parameter(void* parameter) {
136     ASSERT(state_ != DESTROYED);
137     parameter_or_next_free_.parameter = parameter;
138   }
parameter()139   void* parameter() {
140     ASSERT(state_ != DESTROYED);
141     return parameter_or_next_free_.parameter;
142   }
143 
144   // Returns the callback for this weak handle.
callback()145   WeakReferenceCallback callback() { return callback_; }
146 
PostGarbageCollectionProcessing()147   bool PostGarbageCollectionProcessing() {
148     if (state_ != Node::PENDING) return false;
149     LOG(HandleEvent("GlobalHandle::Processing", handle().location()));
150     void* par = parameter();
151     state_ = NEAR_DEATH;
152     set_parameter(NULL);
153     // The callback function is resolved as late as possible to preserve old
154     // behavior.
155     WeakReferenceCallback func = callback();
156     if (func == NULL) return false;
157 
158     v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle());
159     {
160       // Forbid reuse of destroyed nodes as they might be already deallocated.
161       // It's fine though to reuse nodes that were destroyed in weak callback
162       // as those cannot be deallocated until we are back from the callback.
163       set_first_free(NULL);
164       // Leaving V8.
165       VMState state(EXTERNAL);
166       func(object, par);
167     }
168     return true;
169   }
170 
171   // Place the handle address first to avoid offset computation.
172   Object* object_;  // Storage for object pointer.
173 
174   // Transition diagram:
175   // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
176   enum State {
177     NORMAL,      // Normal global handle.
178     WEAK,        // Flagged as weak but not yet finalized.
179     PENDING,     // Has been recognized as only reachable by weak handles.
180     NEAR_DEATH,  // Callback has informed the handle is near death.
181     DESTROYED
182   };
183   State state_;
184 
185  private:
186   // Handle specific callback.
187   WeakReferenceCallback callback_;
188   // Provided data for callback.  In DESTROYED state, this is used for
189   // the free list link.
190   union {
191     void* parameter;
192     Node* next_free;
193   } parameter_or_next_free_;
194 
195   // Linkage for the list.
196   Node* next_;
197 
198  public:
199   TRACK_MEMORY("GlobalHandles::Node")
200 };
201 
202 
Create(Object * value)203 Handle<Object> GlobalHandles::Create(Object* value) {
204   Counters::global_handles.Increment();
205   Node* result;
206   if (first_free() == NULL) {
207     // Allocate a new node.
208     result = new Node(value);
209     result->set_next(head());
210     set_head(result);
211   } else {
212     // Take the first node in the free list.
213     result = first_free();
214     set_first_free(result->next_free());
215     result->Initialize(value);
216   }
217   return result->handle();
218 }
219 
220 
Destroy(Object ** location)221 void GlobalHandles::Destroy(Object** location) {
222   Counters::global_handles.Decrement();
223   if (location == NULL) return;
224   Node* node = Node::FromLocation(location);
225   node->Destroy();
226   // Link the destroyed.
227   node->set_next_free(first_free());
228   set_first_free(node);
229 }
230 
231 
MakeWeak(Object ** location,void * parameter,WeakReferenceCallback callback)232 void GlobalHandles::MakeWeak(Object** location, void* parameter,
233                              WeakReferenceCallback callback) {
234   ASSERT(callback != NULL);
235   Node::FromLocation(location)->MakeWeak(parameter, callback);
236 }
237 
238 
ClearWeakness(Object ** location)239 void GlobalHandles::ClearWeakness(Object** location) {
240   Node::FromLocation(location)->ClearWeakness();
241 }
242 
243 
IsNearDeath(Object ** location)244 bool GlobalHandles::IsNearDeath(Object** location) {
245   return Node::FromLocation(location)->IsNearDeath();
246 }
247 
248 
IsWeak(Object ** location)249 bool GlobalHandles::IsWeak(Object** location) {
250   return Node::FromLocation(location)->IsWeak();
251 }
252 
253 
IterateWeakRoots(ObjectVisitor * v)254 void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
255   // Traversal of GC roots in the global handle list that are marked as
256   // WEAK or PENDING.
257   for (Node* current = head_; current != NULL; current = current->next()) {
258     if (current->state_ == Node::WEAK
259       || current->state_ == Node::PENDING
260       || current->state_ == Node::NEAR_DEATH) {
261       v->VisitPointer(&current->object_);
262     }
263   }
264 }
265 
266 
IdentifyWeakHandles(WeakSlotCallback f)267 void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
268   for (Node* current = head_; current != NULL; current = current->next()) {
269     if (current->state_ == Node::WEAK) {
270       if (f(&current->object_)) {
271         current->state_ = Node::PENDING;
272         LOG(HandleEvent("GlobalHandle::Pending", current->handle().location()));
273       }
274     }
275   }
276 }
277 
278 
279 int post_gc_processing_count = 0;
280 
PostGarbageCollectionProcessing()281 void GlobalHandles::PostGarbageCollectionProcessing() {
282   // Process weak global handle callbacks. This must be done after the
283   // GC is completely done, because the callbacks may invoke arbitrary
284   // API functions.
285   // At the same time deallocate all DESTROYED nodes
286   ASSERT(Heap::gc_state() == Heap::NOT_IN_GC);
287   const int initial_post_gc_processing_count = ++post_gc_processing_count;
288   Node** p = &head_;
289   while (*p != NULL) {
290     if ((*p)->PostGarbageCollectionProcessing()) {
291       if (initial_post_gc_processing_count != post_gc_processing_count) {
292         // Weak callback triggered another GC and another round of
293         // PostGarbageCollection processing.  The current node might
294         // have been deleted in that round, so we need to bail out (or
295         // restart the processing).
296         break;
297       }
298     }
299     if ((*p)->state_ == Node::DESTROYED) {
300       // Delete the link.
301       Node* node = *p;
302       *p = node->next();  // Update the link.
303       delete node;
304     } else {
305       p = (*p)->next_addr();
306     }
307   }
308   set_first_free(NULL);
309 }
310 
311 
IterateRoots(ObjectVisitor * v)312 void GlobalHandles::IterateRoots(ObjectVisitor* v) {
313   // Traversal of global handles marked as NORMAL or NEAR_DEATH.
314   for (Node* current = head_; current != NULL; current = current->next()) {
315     if (current->state_ == Node::NORMAL) {
316       v->VisitPointer(&current->object_);
317     }
318   }
319 }
320 
TearDown()321 void GlobalHandles::TearDown() {
322   // Delete all the nodes in the linked list.
323   Node* current = head_;
324   while (current != NULL) {
325     Node* n = current;
326     current = current->next();
327     delete n;
328   }
329   // Reset the head and free_list.
330   set_head(NULL);
331   set_first_free(NULL);
332 }
333 
334 
335 int GlobalHandles::number_of_weak_handles_ = 0;
336 int GlobalHandles::number_of_global_object_weak_handles_ = 0;
337 
338 GlobalHandles::Node* GlobalHandles::head_ = NULL;
339 GlobalHandles::Node* GlobalHandles::first_free_ = NULL;
340 
341 #ifdef DEBUG
342 
PrintStats()343 void GlobalHandles::PrintStats() {
344   int total = 0;
345   int weak = 0;
346   int pending = 0;
347   int near_death = 0;
348   int destroyed = 0;
349 
350   for (Node* current = head_; current != NULL; current = current->next()) {
351     total++;
352     if (current->state_ == Node::WEAK) weak++;
353     if (current->state_ == Node::PENDING) pending++;
354     if (current->state_ == Node::NEAR_DEATH) near_death++;
355     if (current->state_ == Node::DESTROYED) destroyed++;
356   }
357 
358   PrintF("Global Handle Statistics:\n");
359   PrintF("  allocated memory = %dB\n", sizeof(Node) * total);
360   PrintF("  # weak       = %d\n", weak);
361   PrintF("  # pending    = %d\n", pending);
362   PrintF("  # near_death = %d\n", near_death);
363   PrintF("  # destroyed  = %d\n", destroyed);
364   PrintF("  # total      = %d\n", total);
365 }
366 
Print()367 void GlobalHandles::Print() {
368   PrintF("Global handles:\n");
369   for (Node* current = head_; current != NULL; current = current->next()) {
370     PrintF("  handle %p to %p (weak=%d)\n", current->handle().location(),
371            *current->handle(), current->state_ == Node::WEAK);
372   }
373 }
374 
375 #endif
376 
ObjectGroups()377 List<ObjectGroup*>* GlobalHandles::ObjectGroups() {
378   // Lazily initialize the list to avoid startup time static constructors.
379   static List<ObjectGroup*> groups(4);
380   return &groups;
381 }
382 
AddGroup(Object *** handles,size_t length)383 void GlobalHandles::AddGroup(Object*** handles, size_t length) {
384   ObjectGroup* new_entry = new ObjectGroup(length);
385   for (size_t i = 0; i < length; ++i)
386     new_entry->objects_.Add(handles[i]);
387   ObjectGroups()->Add(new_entry);
388 }
389 
390 
RemoveObjectGroups()391 void GlobalHandles::RemoveObjectGroups() {
392   List<ObjectGroup*>* object_groups = ObjectGroups();
393   for (int i = 0; i< object_groups->length(); i++) {
394     delete object_groups->at(i);
395   }
396   object_groups->Clear();
397 }
398 
399 
400 } }  // namespace v8::internal
401