global-handles.cc revision 3ce2e2076e8e3e60cf1810eec160ea2d8557e9e7
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
33namespace v8 {
34namespace internal {
35
36class 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  explicit Node(Object* object) {
48    Initialize(object);
49    // Initialize link structure.
50    next_ = NULL;
51  }
52
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
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_.
74  Node* next() { return next_; }
75  void set_next(Node* value) { next_ = value; }
76  Node** next_addr() { return &next_; }
77
78  // Accessors for next free node in the free list.
79  Node* next_free() {
80    ASSERT(state_ == DESTROYED);
81    return parameter_or_next_free_.next_free;
82  }
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.
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.
95  Handle<Object> handle() { return Handle<Object>(&object_); }
96
97  // Make this handle weak.
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
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
125  bool IsNearDeath() {
126    // Check for PENDING to ensure correct answer when processing callbacks.
127    return state_ == PENDING || state_ == NEAR_DEATH;
128  }
129
130  bool IsWeak() {
131    return state_ == WEAK;
132  }
133
134  // Returns the id for this weak handle.
135  void set_parameter(void* parameter) {
136    ASSERT(state_ != DESTROYED);
137    parameter_or_next_free_.parameter = parameter;
138  }
139  void* parameter() {
140    ASSERT(state_ != DESTROYED);
141    return parameter_or_next_free_.parameter;
142  }
143
144  // Returns the callback for this weak handle.
145  WeakReferenceCallback callback() { return callback_; }
146
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
203Handle<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
221void 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
232void GlobalHandles::MakeWeak(Object** location, void* parameter,
233                             WeakReferenceCallback callback) {
234  ASSERT(callback != NULL);
235  Node::FromLocation(location)->MakeWeak(parameter, callback);
236}
237
238
239void GlobalHandles::ClearWeakness(Object** location) {
240  Node::FromLocation(location)->ClearWeakness();
241}
242
243
244bool GlobalHandles::IsNearDeath(Object** location) {
245  return Node::FromLocation(location)->IsNearDeath();
246}
247
248
249bool GlobalHandles::IsWeak(Object** location) {
250  return Node::FromLocation(location)->IsWeak();
251}
252
253
254void 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
267void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f,
268                                     WeakReferenceCallback callback) {
269  for (Node* current = head_; current != NULL; current = current->next()) {
270    if (current->IsWeak() && current->callback() == callback) {
271      f(current->object_, current->parameter());
272    }
273  }
274}
275
276
277void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
278  for (Node* current = head_; current != NULL; current = current->next()) {
279    if (current->state_ == Node::WEAK) {
280      if (f(&current->object_)) {
281        current->state_ = Node::PENDING;
282        LOG(HandleEvent("GlobalHandle::Pending", current->handle().location()));
283      }
284    }
285  }
286}
287
288
289int post_gc_processing_count = 0;
290
291void GlobalHandles::PostGarbageCollectionProcessing() {
292  // Process weak global handle callbacks. This must be done after the
293  // GC is completely done, because the callbacks may invoke arbitrary
294  // API functions.
295  // At the same time deallocate all DESTROYED nodes
296  ASSERT(Heap::gc_state() == Heap::NOT_IN_GC);
297  const int initial_post_gc_processing_count = ++post_gc_processing_count;
298  Node** p = &head_;
299  while (*p != NULL) {
300    if ((*p)->PostGarbageCollectionProcessing()) {
301      if (initial_post_gc_processing_count != post_gc_processing_count) {
302        // Weak callback triggered another GC and another round of
303        // PostGarbageCollection processing.  The current node might
304        // have been deleted in that round, so we need to bail out (or
305        // restart the processing).
306        break;
307      }
308    }
309    if ((*p)->state_ == Node::DESTROYED) {
310      // Delete the link.
311      Node* node = *p;
312      *p = node->next();  // Update the link.
313      delete node;
314    } else {
315      p = (*p)->next_addr();
316    }
317  }
318  set_first_free(NULL);
319}
320
321
322void GlobalHandles::IterateRoots(ObjectVisitor* v) {
323  // Traversal of global handles marked as NORMAL or NEAR_DEATH.
324  for (Node* current = head_; current != NULL; current = current->next()) {
325    if (current->state_ == Node::NORMAL) {
326      v->VisitPointer(&current->object_);
327    }
328  }
329}
330
331void GlobalHandles::TearDown() {
332  // Delete all the nodes in the linked list.
333  Node* current = head_;
334  while (current != NULL) {
335    Node* n = current;
336    current = current->next();
337    delete n;
338  }
339  // Reset the head and free_list.
340  set_head(NULL);
341  set_first_free(NULL);
342}
343
344
345int GlobalHandles::number_of_weak_handles_ = 0;
346int GlobalHandles::number_of_global_object_weak_handles_ = 0;
347
348GlobalHandles::Node* GlobalHandles::head_ = NULL;
349GlobalHandles::Node* GlobalHandles::first_free_ = NULL;
350
351#ifdef DEBUG
352
353void GlobalHandles::PrintStats() {
354  int total = 0;
355  int weak = 0;
356  int pending = 0;
357  int near_death = 0;
358  int destroyed = 0;
359
360  for (Node* current = head_; current != NULL; current = current->next()) {
361    total++;
362    if (current->state_ == Node::WEAK) weak++;
363    if (current->state_ == Node::PENDING) pending++;
364    if (current->state_ == Node::NEAR_DEATH) near_death++;
365    if (current->state_ == Node::DESTROYED) destroyed++;
366  }
367
368  PrintF("Global Handle Statistics:\n");
369  PrintF("  allocated memory = %dB\n", sizeof(Node) * total);
370  PrintF("  # weak       = %d\n", weak);
371  PrintF("  # pending    = %d\n", pending);
372  PrintF("  # near_death = %d\n", near_death);
373  PrintF("  # destroyed  = %d\n", destroyed);
374  PrintF("  # total      = %d\n", total);
375}
376
377void GlobalHandles::Print() {
378  PrintF("Global handles:\n");
379  for (Node* current = head_; current != NULL; current = current->next()) {
380    PrintF("  handle %p to %p (weak=%d)\n", current->handle().location(),
381           *current->handle(), current->state_ == Node::WEAK);
382  }
383}
384
385#endif
386
387List<ObjectGroup*>* GlobalHandles::ObjectGroups() {
388  // Lazily initialize the list to avoid startup time static constructors.
389  static List<ObjectGroup*> groups(4);
390  return &groups;
391}
392
393void GlobalHandles::AddGroup(Object*** handles, size_t length) {
394  ObjectGroup* new_entry = new ObjectGroup(length);
395  for (size_t i = 0; i < length; ++i)
396    new_entry->objects_.Add(handles[i]);
397  ObjectGroups()->Add(new_entry);
398}
399
400
401void GlobalHandles::RemoveObjectGroups() {
402  List<ObjectGroup*>* object_groups = ObjectGroups();
403  for (int i = 0; i< object_groups->length(); i++) {
404    delete object_groups->at(i);
405  }
406  object_groups->Clear();
407}
408
409
410} }  // namespace v8::internal
411