blob: f089b85e1ab75f018f2cba05662e606222c6bef1 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// 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
Steve Blockd0582a62009-12-15 09:54:21 +000047 Node() {
48 state_ = DESTROYED;
49 }
50
Steve Blocka7e24c12009-10-30 11:49:00 +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()));
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
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
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
151 bool PostGarbageCollectionProcessing() {
152 if (state_ != Node::PENDING) return false;
153 LOG(HandleEvent("GlobalHandle::Processing", handle().location()));
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100154 WeakReferenceCallback func = callback();
155 if (func == NULL) {
156 Destroy();
157 return false;
158 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000159 void* par = parameter();
160 state_ = NEAR_DEATH;
161 set_parameter(NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +0000162
163 v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle());
164 {
165 // Forbid reuse of destroyed nodes as they might be already deallocated.
166 // It's fine though to reuse nodes that were destroyed in weak callback
167 // as those cannot be deallocated until we are back from the callback.
168 set_first_free(NULL);
Steve Blockd0582a62009-12-15 09:54:21 +0000169 if (first_deallocated()) {
170 first_deallocated()->set_next(head());
171 }
Leon Clarkee46be812010-01-19 14:06:41 +0000172 // Check that we are not passing a finalized external string to
173 // the callback.
174 ASSERT(!object_->IsExternalAsciiString() ||
175 ExternalAsciiString::cast(object_)->resource() != NULL);
176 ASSERT(!object_->IsExternalTwoByteString() ||
177 ExternalTwoByteString::cast(object_)->resource() != NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +0000178 // Leaving V8.
179 VMState state(EXTERNAL);
180 func(object, par);
181 }
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100182 // Absense of explicit cleanup or revival of weak handle
183 // in most of the cases would lead to memory leak.
184 ASSERT(state_ != NEAR_DEATH);
Steve Blocka7e24c12009-10-30 11:49:00 +0000185 return true;
186 }
187
188 // Place the handle address first to avoid offset computation.
189 Object* object_; // Storage for object pointer.
190
191 // Transition diagram:
192 // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
193 enum State {
194 NORMAL, // Normal global handle.
195 WEAK, // Flagged as weak but not yet finalized.
196 PENDING, // Has been recognized as only reachable by weak handles.
197 NEAR_DEATH, // Callback has informed the handle is near death.
198 DESTROYED
199 };
200 State state_;
201
202 private:
203 // Handle specific callback.
204 WeakReferenceCallback callback_;
205 // Provided data for callback. In DESTROYED state, this is used for
206 // the free list link.
207 union {
208 void* parameter;
209 Node* next_free;
210 } parameter_or_next_free_;
211
212 // Linkage for the list.
213 Node* next_;
214
215 public:
216 TRACK_MEMORY("GlobalHandles::Node")
217};
218
219
Steve Blockd0582a62009-12-15 09:54:21 +0000220class GlobalHandles::Pool BASE_EMBEDDED {
221 public:
222 Pool() {
223 current_ = new Chunk();
224 current_->previous = NULL;
225 next_ = current_->nodes;
226 limit_ = current_->nodes + kNodesPerChunk;
227 }
228
Ben Murdochbb769b22010-08-11 14:56:33 +0100229 ~Pool() {
230 if (current_ != NULL) {
231 Release();
232 }
233 }
234
Steve Blockd0582a62009-12-15 09:54:21 +0000235 Node* Allocate() {
236 if (next_ < limit_) {
237 return next_++;
238 }
239 return SlowAllocate();
240 }
241
242 void Release() {
243 Chunk* current = current_;
244 ASSERT(current != NULL); // At least a single block must by allocated
245 do {
246 Chunk* previous = current->previous;
247 delete current;
248 current = previous;
249 } while (current != NULL);
250 current_ = NULL;
251 next_ = limit_ = NULL;
252 }
253
254 private:
255 static const int kNodesPerChunk = (1 << 12) - 1;
256 struct Chunk : public Malloced {
257 Chunk* previous;
258 Node nodes[kNodesPerChunk];
259 };
260
261 Node* SlowAllocate() {
262 Chunk* chunk = new Chunk();
263 chunk->previous = current_;
264 current_ = chunk;
265
266 Node* new_nodes = current_->nodes;
267 next_ = new_nodes + 1;
268 limit_ = new_nodes + kNodesPerChunk;
269 return new_nodes;
270 }
271
272 Chunk* current_;
273 Node* next_;
274 Node* limit_;
275};
276
277
278static GlobalHandles::Pool pool_;
279
280
Steve Blocka7e24c12009-10-30 11:49:00 +0000281Handle<Object> GlobalHandles::Create(Object* value) {
282 Counters::global_handles.Increment();
283 Node* result;
Steve Blockd0582a62009-12-15 09:54:21 +0000284 if (first_free()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000285 // Take the first node in the free list.
286 result = first_free();
287 set_first_free(result->next_free());
Steve Blockd0582a62009-12-15 09:54:21 +0000288 } else if (first_deallocated()) {
289 // Next try deallocated list
290 result = first_deallocated();
291 set_first_deallocated(result->next_free());
292 ASSERT(result->next() == head());
293 set_head(result);
294 } else {
295 // Allocate a new node.
296 result = pool_.Allocate();
297 result->set_next(head());
298 set_head(result);
Steve Blocka7e24c12009-10-30 11:49:00 +0000299 }
Steve Blockd0582a62009-12-15 09:54:21 +0000300 result->Initialize(value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000301 return result->handle();
302}
303
304
305void GlobalHandles::Destroy(Object** location) {
306 Counters::global_handles.Decrement();
307 if (location == NULL) return;
308 Node* node = Node::FromLocation(location);
309 node->Destroy();
310 // Link the destroyed.
311 node->set_next_free(first_free());
312 set_first_free(node);
313}
314
315
316void GlobalHandles::MakeWeak(Object** location, void* parameter,
317 WeakReferenceCallback callback) {
318 ASSERT(callback != NULL);
319 Node::FromLocation(location)->MakeWeak(parameter, callback);
320}
321
322
323void GlobalHandles::ClearWeakness(Object** location) {
324 Node::FromLocation(location)->ClearWeakness();
325}
326
327
328bool GlobalHandles::IsNearDeath(Object** location) {
329 return Node::FromLocation(location)->IsNearDeath();
330}
331
332
333bool GlobalHandles::IsWeak(Object** location) {
334 return Node::FromLocation(location)->IsWeak();
335}
336
337
338void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
339 // Traversal of GC roots in the global handle list that are marked as
340 // WEAK or PENDING.
341 for (Node* current = head_; current != NULL; current = current->next()) {
342 if (current->state_ == Node::WEAK
343 || current->state_ == Node::PENDING
344 || current->state_ == Node::NEAR_DEATH) {
345 v->VisitPointer(&current->object_);
346 }
347 }
348}
349
350
Steve Block3ce2e202009-11-05 08:53:23 +0000351void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f,
352 WeakReferenceCallback callback) {
353 for (Node* current = head_; current != NULL; current = current->next()) {
354 if (current->IsWeak() && current->callback() == callback) {
355 f(current->object_, current->parameter());
356 }
357 }
358}
359
360
Steve Blocka7e24c12009-10-30 11:49:00 +0000361void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
362 for (Node* current = head_; current != NULL; current = current->next()) {
363 if (current->state_ == Node::WEAK) {
364 if (f(&current->object_)) {
365 current->state_ = Node::PENDING;
366 LOG(HandleEvent("GlobalHandle::Pending", current->handle().location()));
367 }
368 }
369 }
370}
371
372
373int post_gc_processing_count = 0;
374
375void GlobalHandles::PostGarbageCollectionProcessing() {
376 // Process weak global handle callbacks. This must be done after the
377 // GC is completely done, because the callbacks may invoke arbitrary
378 // API functions.
Steve Blockd0582a62009-12-15 09:54:21 +0000379 // At the same time deallocate all DESTROYED nodes.
Steve Blocka7e24c12009-10-30 11:49:00 +0000380 ASSERT(Heap::gc_state() == Heap::NOT_IN_GC);
381 const int initial_post_gc_processing_count = ++post_gc_processing_count;
382 Node** p = &head_;
383 while (*p != NULL) {
384 if ((*p)->PostGarbageCollectionProcessing()) {
385 if (initial_post_gc_processing_count != post_gc_processing_count) {
386 // Weak callback triggered another GC and another round of
387 // PostGarbageCollection processing. The current node might
388 // have been deleted in that round, so we need to bail out (or
389 // restart the processing).
390 break;
391 }
392 }
393 if ((*p)->state_ == Node::DESTROYED) {
394 // Delete the link.
395 Node* node = *p;
396 *p = node->next(); // Update the link.
Steve Blockd0582a62009-12-15 09:54:21 +0000397 if (first_deallocated()) {
398 first_deallocated()->set_next(node);
399 }
400 node->set_next_free(first_deallocated());
401 set_first_deallocated(node);
Steve Blocka7e24c12009-10-30 11:49:00 +0000402 } else {
403 p = (*p)->next_addr();
404 }
405 }
406 set_first_free(NULL);
Steve Blockd0582a62009-12-15 09:54:21 +0000407 if (first_deallocated()) {
408 first_deallocated()->set_next(head());
409 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000410}
411
412
Steve Blockd0582a62009-12-15 09:54:21 +0000413void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) {
414 // Traversal of global handles marked as NORMAL.
Steve Blocka7e24c12009-10-30 11:49:00 +0000415 for (Node* current = head_; current != NULL; current = current->next()) {
416 if (current->state_ == Node::NORMAL) {
417 v->VisitPointer(&current->object_);
418 }
419 }
420}
421
Steve Blockd0582a62009-12-15 09:54:21 +0000422
423void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
424 for (Node* current = head_; current != NULL; current = current->next()) {
425 if (current->state_ != Node::DESTROYED) {
426 v->VisitPointer(&current->object_);
427 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000428 }
Steve Blockd0582a62009-12-15 09:54:21 +0000429}
430
431
432void GlobalHandles::TearDown() {
433 // Reset all the lists.
Steve Blocka7e24c12009-10-30 11:49:00 +0000434 set_head(NULL);
435 set_first_free(NULL);
Steve Blockd0582a62009-12-15 09:54:21 +0000436 set_first_deallocated(NULL);
437 pool_.Release();
Steve Blocka7e24c12009-10-30 11:49:00 +0000438}
439
440
441int GlobalHandles::number_of_weak_handles_ = 0;
442int GlobalHandles::number_of_global_object_weak_handles_ = 0;
443
444GlobalHandles::Node* GlobalHandles::head_ = NULL;
445GlobalHandles::Node* GlobalHandles::first_free_ = NULL;
Steve Blockd0582a62009-12-15 09:54:21 +0000446GlobalHandles::Node* GlobalHandles::first_deallocated_ = NULL;
447
448void GlobalHandles::RecordStats(HeapStats* stats) {
449 *stats->global_handle_count = 0;
450 *stats->weak_global_handle_count = 0;
451 *stats->pending_global_handle_count = 0;
452 *stats->near_death_global_handle_count = 0;
453 *stats->destroyed_global_handle_count = 0;
454 for (Node* current = head_; current != NULL; current = current->next()) {
455 *stats->global_handle_count += 1;
456 if (current->state_ == Node::WEAK) {
457 *stats->weak_global_handle_count += 1;
458 } else if (current->state_ == Node::PENDING) {
459 *stats->pending_global_handle_count += 1;
460 } else if (current->state_ == Node::NEAR_DEATH) {
461 *stats->near_death_global_handle_count += 1;
462 } else if (current->state_ == Node::DESTROYED) {
463 *stats->destroyed_global_handle_count += 1;
464 }
465 }
466}
Steve Blocka7e24c12009-10-30 11:49:00 +0000467
468#ifdef DEBUG
469
470void GlobalHandles::PrintStats() {
471 int total = 0;
472 int weak = 0;
473 int pending = 0;
474 int near_death = 0;
475 int destroyed = 0;
476
477 for (Node* current = head_; current != NULL; current = current->next()) {
478 total++;
479 if (current->state_ == Node::WEAK) weak++;
480 if (current->state_ == Node::PENDING) pending++;
481 if (current->state_ == Node::NEAR_DEATH) near_death++;
482 if (current->state_ == Node::DESTROYED) destroyed++;
483 }
484
485 PrintF("Global Handle Statistics:\n");
486 PrintF(" allocated memory = %dB\n", sizeof(Node) * total);
487 PrintF(" # weak = %d\n", weak);
488 PrintF(" # pending = %d\n", pending);
489 PrintF(" # near_death = %d\n", near_death);
490 PrintF(" # destroyed = %d\n", destroyed);
491 PrintF(" # total = %d\n", total);
492}
493
494void GlobalHandles::Print() {
495 PrintF("Global handles:\n");
496 for (Node* current = head_; current != NULL; current = current->next()) {
497 PrintF(" handle %p to %p (weak=%d)\n", current->handle().location(),
498 *current->handle(), current->state_ == Node::WEAK);
499 }
500}
501
502#endif
503
504List<ObjectGroup*>* GlobalHandles::ObjectGroups() {
505 // Lazily initialize the list to avoid startup time static constructors.
506 static List<ObjectGroup*> groups(4);
507 return &groups;
508}
509
510void GlobalHandles::AddGroup(Object*** handles, size_t length) {
511 ObjectGroup* new_entry = new ObjectGroup(length);
512 for (size_t i = 0; i < length; ++i)
513 new_entry->objects_.Add(handles[i]);
514 ObjectGroups()->Add(new_entry);
515}
516
517
518void GlobalHandles::RemoveObjectGroups() {
519 List<ObjectGroup*>* object_groups = ObjectGroups();
520 for (int i = 0; i< object_groups->length(); i++) {
521 delete object_groups->at(i);
522 }
523 object_groups->Clear();
524}
525
Steve Blocka7e24c12009-10-30 11:49:00 +0000526} } // namespace v8::internal