blob: c6cc288e6abd33289c10c36a4e1ad3c94eb6bbd0 [file] [log] [blame]
ager@chromium.org71daaf62009-04-01 07:22:49 +00001// Copyright 2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// 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
kasperl@chromium.org71affb52009-05-26 05:44:31 +000033namespace v8 {
34namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000035
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
ager@chromium.org3811b432009-10-28 14:53:37 +000047 Node() {
48 state_ = DESTROYED;
49 }
50
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000051 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()));
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000104 ASSERT(state_ != DESTROYED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000105 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()));
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000118 ASSERT(state_ != DESTROYED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000119 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
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000151 bool PostGarbageCollectionProcessing() {
152 if (state_ != Node::PENDING) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000153 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();
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000160 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 // Leaving V8.
169 VMState state(EXTERNAL);
170 func(object, par);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000171 }
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000172 return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000173 }
174
175 // Place the handle address first to avoid offset computation.
176 Object* object_; // Storage for object pointer.
177
178 // Transition diagram:
179 // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
180 enum State {
181 NORMAL, // Normal global handle.
182 WEAK, // Flagged as weak but not yet finalized.
183 PENDING, // Has been recognized as only reachable by weak handles.
184 NEAR_DEATH, // Callback has informed the handle is near death.
185 DESTROYED
186 };
187 State state_;
188
189 private:
190 // Handle specific callback.
191 WeakReferenceCallback callback_;
192 // Provided data for callback. In DESTROYED state, this is used for
193 // the free list link.
194 union {
195 void* parameter;
196 Node* next_free;
197 } parameter_or_next_free_;
198
199 // Linkage for the list.
200 Node* next_;
201
202 public:
203 TRACK_MEMORY("GlobalHandles::Node")
204};
205
206
ager@chromium.org3811b432009-10-28 14:53:37 +0000207class GlobalHandles::Pool BASE_EMBEDDED {
208 public:
209 Pool() {
210 current_ = new Chunk();
211 current_->previous = NULL;
212 next_ = current_->nodes;
213 limit_ = current_->nodes + kNodesPerChunk;
214 }
215
216 Node* Allocate() {
217 if (next_ < limit_) {
218 return next_++;
219 }
220 return SlowAllocate();
221 }
222
223 void Release() {
224 Chunk* current = current_;
225 ASSERT(current != NULL); // At least a single block must by allocated
226 do {
227 Chunk* previous = current->previous;
228 delete current;
229 current = previous;
230 } while (current != NULL);
231 current_ = NULL;
232 next_ = limit_ = NULL;
233 }
234
235 private:
236 static const int kNodesPerChunk = (1 << 12) - 1;
237 struct Chunk : public Malloced {
238 Chunk* previous;
239 Node nodes[kNodesPerChunk];
240 };
241
242 Node* SlowAllocate() {
243 Chunk* chunk = new Chunk();
244 chunk->previous = current_;
245 current_ = chunk;
246
247 Node* new_nodes = current_->nodes;
248 next_ = new_nodes + 1;
249 limit_ = new_nodes + kNodesPerChunk;
250 return new_nodes;
251 }
252
253 Chunk* current_;
254 Node* next_;
255 Node* limit_;
256};
257
258
259static GlobalHandles::Pool pool_;
260
261
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000262Handle<Object> GlobalHandles::Create(Object* value) {
263 Counters::global_handles.Increment();
264 Node* result;
ager@chromium.org3811b432009-10-28 14:53:37 +0000265 if (first_free()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266 // Take the first node in the free list.
267 result = first_free();
268 set_first_free(result->next_free());
ager@chromium.org3811b432009-10-28 14:53:37 +0000269 } else if (first_deallocated()) {
270 // Next try deallocated list
271 result = first_deallocated();
272 set_first_deallocated(result->next_free());
273 set_head(result);
274 } else {
275 // Allocate a new node.
276 result = pool_.Allocate();
277 result->set_next(head());
278 set_head(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000279 }
ager@chromium.org3811b432009-10-28 14:53:37 +0000280 result->Initialize(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000281 return result->handle();
282}
283
284
285void GlobalHandles::Destroy(Object** location) {
286 Counters::global_handles.Decrement();
287 if (location == NULL) return;
288 Node* node = Node::FromLocation(location);
289 node->Destroy();
290 // Link the destroyed.
291 node->set_next_free(first_free());
292 set_first_free(node);
293}
294
295
296void GlobalHandles::MakeWeak(Object** location, void* parameter,
297 WeakReferenceCallback callback) {
298 ASSERT(callback != NULL);
299 Node::FromLocation(location)->MakeWeak(parameter, callback);
300}
301
302
303void GlobalHandles::ClearWeakness(Object** location) {
304 Node::FromLocation(location)->ClearWeakness();
305}
306
307
308bool GlobalHandles::IsNearDeath(Object** location) {
309 return Node::FromLocation(location)->IsNearDeath();
310}
311
312
313bool GlobalHandles::IsWeak(Object** location) {
314 return Node::FromLocation(location)->IsWeak();
315}
316
317
318void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
319 // Traversal of GC roots in the global handle list that are marked as
320 // WEAK or PENDING.
321 for (Node* current = head_; current != NULL; current = current->next()) {
322 if (current->state_ == Node::WEAK
323 || current->state_ == Node::PENDING
324 || current->state_ == Node::NEAR_DEATH) {
325 v->VisitPointer(&current->object_);
326 }
327 }
328}
329
330
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000331void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f,
332 WeakReferenceCallback callback) {
333 for (Node* current = head_; current != NULL; current = current->next()) {
334 if (current->IsWeak() && current->callback() == callback) {
335 f(current->object_, current->parameter());
336 }
337 }
338}
339
340
ager@chromium.org9085a012009-05-11 19:22:57 +0000341void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000342 for (Node* current = head_; current != NULL; current = current->next()) {
343 if (current->state_ == Node::WEAK) {
344 if (f(&current->object_)) {
345 current->state_ = Node::PENDING;
346 LOG(HandleEvent("GlobalHandle::Pending", current->handle().location()));
347 }
348 }
349 }
350}
351
352
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000353int post_gc_processing_count = 0;
354
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000355void GlobalHandles::PostGarbageCollectionProcessing() {
356 // Process weak global handle callbacks. This must be done after the
357 // GC is completely done, because the callbacks may invoke arbitrary
358 // API functions.
ager@chromium.org3811b432009-10-28 14:53:37 +0000359 // At the same time deallocate all DESTROYED nodes.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000360 ASSERT(Heap::gc_state() == Heap::NOT_IN_GC);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000361 const int initial_post_gc_processing_count = ++post_gc_processing_count;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000362 Node** p = &head_;
363 while (*p != NULL) {
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000364 if ((*p)->PostGarbageCollectionProcessing()) {
365 if (initial_post_gc_processing_count != post_gc_processing_count) {
366 // Weak callback triggered another GC and another round of
367 // PostGarbageCollection processing. The current node might
368 // have been deleted in that round, so we need to bail out (or
369 // restart the processing).
370 break;
371 }
372 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000373 if ((*p)->state_ == Node::DESTROYED) {
374 // Delete the link.
375 Node* node = *p;
376 *p = node->next(); // Update the link.
ager@chromium.org3811b432009-10-28 14:53:37 +0000377 if (first_deallocated()) {
378 first_deallocated()->set_next(node);
379 }
380 node->set_next_free(first_deallocated());
381 set_first_deallocated(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000382 } else {
383 p = (*p)->next_addr();
384 }
385 }
386 set_first_free(NULL);
ager@chromium.org3811b432009-10-28 14:53:37 +0000387 if (first_deallocated()) {
388 first_deallocated()->set_next(head());
389 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000390}
391
392
393void GlobalHandles::IterateRoots(ObjectVisitor* v) {
394 // Traversal of global handles marked as NORMAL or NEAR_DEATH.
395 for (Node* current = head_; current != NULL; current = current->next()) {
396 if (current->state_ == Node::NORMAL) {
397 v->VisitPointer(&current->object_);
398 }
399 }
400}
401
402void GlobalHandles::TearDown() {
ager@chromium.org3811b432009-10-28 14:53:37 +0000403 // Reset all the lists.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000404 set_head(NULL);
405 set_first_free(NULL);
ager@chromium.org3811b432009-10-28 14:53:37 +0000406 set_first_deallocated(NULL);
407 pool_.Release();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000408}
409
410
411int GlobalHandles::number_of_weak_handles_ = 0;
412int GlobalHandles::number_of_global_object_weak_handles_ = 0;
413
414GlobalHandles::Node* GlobalHandles::head_ = NULL;
415GlobalHandles::Node* GlobalHandles::first_free_ = NULL;
ager@chromium.org3811b432009-10-28 14:53:37 +0000416GlobalHandles::Node* GlobalHandles::first_deallocated_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000417
418#ifdef DEBUG
419
420void GlobalHandles::PrintStats() {
421 int total = 0;
422 int weak = 0;
423 int pending = 0;
424 int near_death = 0;
425 int destroyed = 0;
426
427 for (Node* current = head_; current != NULL; current = current->next()) {
428 total++;
429 if (current->state_ == Node::WEAK) weak++;
430 if (current->state_ == Node::PENDING) pending++;
431 if (current->state_ == Node::NEAR_DEATH) near_death++;
432 if (current->state_ == Node::DESTROYED) destroyed++;
433 }
434
435 PrintF("Global Handle Statistics:\n");
436 PrintF(" allocated memory = %dB\n", sizeof(Node) * total);
437 PrintF(" # weak = %d\n", weak);
438 PrintF(" # pending = %d\n", pending);
439 PrintF(" # near_death = %d\n", near_death);
440 PrintF(" # destroyed = %d\n", destroyed);
441 PrintF(" # total = %d\n", total);
442}
443
444void GlobalHandles::Print() {
445 PrintF("Global handles:\n");
446 for (Node* current = head_; current != NULL; current = current->next()) {
447 PrintF(" handle %p to %p (weak=%d)\n", current->handle().location(),
448 *current->handle(), current->state_ == Node::WEAK);
449 }
450}
451
452#endif
453
ager@chromium.org8bb60582008-12-11 12:02:20 +0000454List<ObjectGroup*>* GlobalHandles::ObjectGroups() {
455 // Lazily initialize the list to avoid startup time static constructors.
456 static List<ObjectGroup*> groups(4);
457 return &groups;
458}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000459
ager@chromium.org8bb60582008-12-11 12:02:20 +0000460void GlobalHandles::AddGroup(Object*** handles, size_t length) {
461 ObjectGroup* new_entry = new ObjectGroup(length);
462 for (size_t i = 0; i < length; ++i)
463 new_entry->objects_.Add(handles[i]);
464 ObjectGroups()->Add(new_entry);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000465}
466
467
468void GlobalHandles::RemoveObjectGroups() {
ager@chromium.org8bb60582008-12-11 12:02:20 +0000469 List<ObjectGroup*>* object_groups = ObjectGroups();
470 for (int i = 0; i< object_groups->length(); i++) {
471 delete object_groups->at(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000472 }
ager@chromium.org8bb60582008-12-11 12:02:20 +0000473 object_groups->Clear();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000474}
475
476
477} } // namespace v8::internal