blob: 18cdc5a3af5615e559e6d91faed474ef7430c8c5 [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
Ben Murdochb0fe1622011-05-05 13:52:32 +010033#include "vm-state-inl.h"
34
Steve Blocka7e24c12009-10-30 11:49:00 +000035namespace v8 {
36namespace internal {
37
38class GlobalHandles::Node : public Malloced {
39 public:
40
41 void Initialize(Object* object) {
42 // Set the initial value of the handle.
43 object_ = object;
44 state_ = NORMAL;
45 parameter_or_next_free_.parameter = NULL;
46 callback_ = NULL;
47 }
48
Steve Blockd0582a62009-12-15 09:54:21 +000049 Node() {
50 state_ = DESTROYED;
51 }
52
Steve Blocka7e24c12009-10-30 11:49:00 +000053 explicit Node(Object* object) {
54 Initialize(object);
55 // Initialize link structure.
56 next_ = NULL;
57 }
58
59 ~Node() {
60 if (state_ != DESTROYED) Destroy();
61#ifdef DEBUG
62 // Zap the values for eager trapping.
63 object_ = NULL;
64 next_ = NULL;
65 parameter_or_next_free_.next_free = NULL;
66#endif
67 }
68
69 void Destroy() {
70 if (state_ == WEAK || IsNearDeath()) {
71 GlobalHandles::number_of_weak_handles_--;
72 if (object_->IsJSGlobalObject()) {
73 GlobalHandles::number_of_global_object_weak_handles_--;
74 }
75 }
76 state_ = DESTROYED;
77 }
78
79 // Accessors for next_.
80 Node* next() { return next_; }
81 void set_next(Node* value) { next_ = value; }
82 Node** next_addr() { return &next_; }
83
84 // Accessors for next free node in the free list.
85 Node* next_free() {
86 ASSERT(state_ == DESTROYED);
87 return parameter_or_next_free_.next_free;
88 }
89 void set_next_free(Node* value) {
90 ASSERT(state_ == DESTROYED);
91 parameter_or_next_free_.next_free = value;
92 }
93
94 // Returns a link from the handle.
95 static Node* FromLocation(Object** location) {
96 ASSERT(OFFSET_OF(Node, object_) == 0);
97 return reinterpret_cast<Node*>(location);
98 }
99
100 // Returns the handle.
101 Handle<Object> handle() { return Handle<Object>(&object_); }
102
103 // Make this handle weak.
104 void MakeWeak(void* parameter, WeakReferenceCallback callback) {
105 LOG(HandleEvent("GlobalHandle::MakeWeak", handle().location()));
106 ASSERT(state_ != DESTROYED);
107 if (state_ != WEAK && !IsNearDeath()) {
108 GlobalHandles::number_of_weak_handles_++;
109 if (object_->IsJSGlobalObject()) {
110 GlobalHandles::number_of_global_object_weak_handles_++;
111 }
112 }
113 state_ = WEAK;
114 set_parameter(parameter);
115 callback_ = callback;
116 }
117
118 void ClearWeakness() {
119 LOG(HandleEvent("GlobalHandle::ClearWeakness", handle().location()));
120 ASSERT(state_ != DESTROYED);
121 if (state_ == WEAK || IsNearDeath()) {
122 GlobalHandles::number_of_weak_handles_--;
123 if (object_->IsJSGlobalObject()) {
124 GlobalHandles::number_of_global_object_weak_handles_--;
125 }
126 }
127 state_ = NORMAL;
128 set_parameter(NULL);
129 }
130
131 bool IsNearDeath() {
132 // Check for PENDING to ensure correct answer when processing callbacks.
133 return state_ == PENDING || state_ == NEAR_DEATH;
134 }
135
136 bool IsWeak() {
137 return state_ == WEAK;
138 }
139
140 // Returns the id for this weak handle.
141 void set_parameter(void* parameter) {
142 ASSERT(state_ != DESTROYED);
143 parameter_or_next_free_.parameter = parameter;
144 }
145 void* parameter() {
146 ASSERT(state_ != DESTROYED);
147 return parameter_or_next_free_.parameter;
148 }
149
150 // Returns the callback for this weak handle.
151 WeakReferenceCallback callback() { return callback_; }
152
153 bool PostGarbageCollectionProcessing() {
154 if (state_ != Node::PENDING) return false;
155 LOG(HandleEvent("GlobalHandle::Processing", handle().location()));
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100156 WeakReferenceCallback func = callback();
157 if (func == NULL) {
158 Destroy();
159 return false;
160 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000161 void* par = parameter();
162 state_ = NEAR_DEATH;
163 set_parameter(NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +0000164
165 v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle());
166 {
167 // Forbid reuse of destroyed nodes as they might be already deallocated.
168 // It's fine though to reuse nodes that were destroyed in weak callback
169 // as those cannot be deallocated until we are back from the callback.
170 set_first_free(NULL);
Steve Blockd0582a62009-12-15 09:54:21 +0000171 if (first_deallocated()) {
172 first_deallocated()->set_next(head());
173 }
Leon Clarkee46be812010-01-19 14:06:41 +0000174 // Check that we are not passing a finalized external string to
175 // the callback.
176 ASSERT(!object_->IsExternalAsciiString() ||
177 ExternalAsciiString::cast(object_)->resource() != NULL);
178 ASSERT(!object_->IsExternalTwoByteString() ||
179 ExternalTwoByteString::cast(object_)->resource() != NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +0000180 // Leaving V8.
181 VMState state(EXTERNAL);
182 func(object, par);
183 }
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100184 // Absense of explicit cleanup or revival of weak handle
185 // in most of the cases would lead to memory leak.
186 ASSERT(state_ != NEAR_DEATH);
Steve Blocka7e24c12009-10-30 11:49:00 +0000187 return true;
188 }
189
190 // Place the handle address first to avoid offset computation.
191 Object* object_; // Storage for object pointer.
192
193 // Transition diagram:
194 // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
195 enum State {
196 NORMAL, // Normal global handle.
197 WEAK, // Flagged as weak but not yet finalized.
198 PENDING, // Has been recognized as only reachable by weak handles.
199 NEAR_DEATH, // Callback has informed the handle is near death.
200 DESTROYED
201 };
202 State state_;
203
204 private:
205 // Handle specific callback.
206 WeakReferenceCallback callback_;
207 // Provided data for callback. In DESTROYED state, this is used for
208 // the free list link.
209 union {
210 void* parameter;
211 Node* next_free;
212 } parameter_or_next_free_;
213
214 // Linkage for the list.
215 Node* next_;
216
217 public:
218 TRACK_MEMORY("GlobalHandles::Node")
219};
220
221
Steve Blockd0582a62009-12-15 09:54:21 +0000222class GlobalHandles::Pool BASE_EMBEDDED {
223 public:
224 Pool() {
225 current_ = new Chunk();
226 current_->previous = NULL;
227 next_ = current_->nodes;
228 limit_ = current_->nodes + kNodesPerChunk;
229 }
230
Ben Murdochbb769b22010-08-11 14:56:33 +0100231 ~Pool() {
232 if (current_ != NULL) {
233 Release();
234 }
235 }
236
Steve Blockd0582a62009-12-15 09:54:21 +0000237 Node* Allocate() {
238 if (next_ < limit_) {
239 return next_++;
240 }
241 return SlowAllocate();
242 }
243
244 void Release() {
245 Chunk* current = current_;
246 ASSERT(current != NULL); // At least a single block must by allocated
247 do {
248 Chunk* previous = current->previous;
249 delete current;
250 current = previous;
251 } while (current != NULL);
252 current_ = NULL;
253 next_ = limit_ = NULL;
254 }
255
256 private:
257 static const int kNodesPerChunk = (1 << 12) - 1;
258 struct Chunk : public Malloced {
259 Chunk* previous;
260 Node nodes[kNodesPerChunk];
261 };
262
263 Node* SlowAllocate() {
264 Chunk* chunk = new Chunk();
265 chunk->previous = current_;
266 current_ = chunk;
267
268 Node* new_nodes = current_->nodes;
269 next_ = new_nodes + 1;
270 limit_ = new_nodes + kNodesPerChunk;
271 return new_nodes;
272 }
273
274 Chunk* current_;
275 Node* next_;
276 Node* limit_;
277};
278
279
280static GlobalHandles::Pool pool_;
281
282
Steve Blocka7e24c12009-10-30 11:49:00 +0000283Handle<Object> GlobalHandles::Create(Object* value) {
284 Counters::global_handles.Increment();
285 Node* result;
Steve Blockd0582a62009-12-15 09:54:21 +0000286 if (first_free()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000287 // Take the first node in the free list.
288 result = first_free();
289 set_first_free(result->next_free());
Steve Blockd0582a62009-12-15 09:54:21 +0000290 } else if (first_deallocated()) {
291 // Next try deallocated list
292 result = first_deallocated();
293 set_first_deallocated(result->next_free());
294 ASSERT(result->next() == head());
295 set_head(result);
296 } else {
297 // Allocate a new node.
298 result = pool_.Allocate();
299 result->set_next(head());
300 set_head(result);
Steve Blocka7e24c12009-10-30 11:49:00 +0000301 }
Steve Blockd0582a62009-12-15 09:54:21 +0000302 result->Initialize(value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000303 return result->handle();
304}
305
306
307void GlobalHandles::Destroy(Object** location) {
308 Counters::global_handles.Decrement();
309 if (location == NULL) return;
310 Node* node = Node::FromLocation(location);
311 node->Destroy();
312 // Link the destroyed.
313 node->set_next_free(first_free());
314 set_first_free(node);
315}
316
317
318void GlobalHandles::MakeWeak(Object** location, void* parameter,
319 WeakReferenceCallback callback) {
320 ASSERT(callback != NULL);
321 Node::FromLocation(location)->MakeWeak(parameter, callback);
322}
323
324
325void GlobalHandles::ClearWeakness(Object** location) {
326 Node::FromLocation(location)->ClearWeakness();
327}
328
329
330bool GlobalHandles::IsNearDeath(Object** location) {
331 return Node::FromLocation(location)->IsNearDeath();
332}
333
334
335bool GlobalHandles::IsWeak(Object** location) {
336 return Node::FromLocation(location)->IsWeak();
337}
338
339
340void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
341 // Traversal of GC roots in the global handle list that are marked as
342 // WEAK or PENDING.
343 for (Node* current = head_; current != NULL; current = current->next()) {
344 if (current->state_ == Node::WEAK
345 || current->state_ == Node::PENDING
346 || current->state_ == Node::NEAR_DEATH) {
347 v->VisitPointer(&current->object_);
348 }
349 }
350}
351
352
Steve Block3ce2e202009-11-05 08:53:23 +0000353void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f,
354 WeakReferenceCallback callback) {
355 for (Node* current = head_; current != NULL; current = current->next()) {
356 if (current->IsWeak() && current->callback() == callback) {
357 f(current->object_, current->parameter());
358 }
359 }
360}
361
362
Steve Blocka7e24c12009-10-30 11:49:00 +0000363void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
364 for (Node* current = head_; current != NULL; current = current->next()) {
365 if (current->state_ == Node::WEAK) {
366 if (f(&current->object_)) {
367 current->state_ = Node::PENDING;
368 LOG(HandleEvent("GlobalHandle::Pending", current->handle().location()));
369 }
370 }
371 }
372}
373
374
375int post_gc_processing_count = 0;
376
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -0800377bool GlobalHandles::PostGarbageCollectionProcessing() {
Steve Blocka7e24c12009-10-30 11:49:00 +0000378 // Process weak global handle callbacks. This must be done after the
379 // GC is completely done, because the callbacks may invoke arbitrary
380 // API functions.
Steve Blockd0582a62009-12-15 09:54:21 +0000381 // At the same time deallocate all DESTROYED nodes.
Steve Blocka7e24c12009-10-30 11:49:00 +0000382 ASSERT(Heap::gc_state() == Heap::NOT_IN_GC);
383 const int initial_post_gc_processing_count = ++post_gc_processing_count;
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -0800384 bool next_gc_likely_to_collect_more = false;
Steve Blocka7e24c12009-10-30 11:49:00 +0000385 Node** p = &head_;
386 while (*p != NULL) {
387 if ((*p)->PostGarbageCollectionProcessing()) {
388 if (initial_post_gc_processing_count != post_gc_processing_count) {
389 // Weak callback triggered another GC and another round of
390 // PostGarbageCollection processing. The current node might
391 // have been deleted in that round, so we need to bail out (or
392 // restart the processing).
393 break;
394 }
395 }
396 if ((*p)->state_ == Node::DESTROYED) {
397 // Delete the link.
398 Node* node = *p;
399 *p = node->next(); // Update the link.
Steve Blockd0582a62009-12-15 09:54:21 +0000400 if (first_deallocated()) {
401 first_deallocated()->set_next(node);
402 }
403 node->set_next_free(first_deallocated());
404 set_first_deallocated(node);
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -0800405 next_gc_likely_to_collect_more = true;
Steve Blocka7e24c12009-10-30 11:49:00 +0000406 } else {
407 p = (*p)->next_addr();
408 }
409 }
410 set_first_free(NULL);
Steve Blockd0582a62009-12-15 09:54:21 +0000411 if (first_deallocated()) {
412 first_deallocated()->set_next(head());
413 }
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -0800414
415 return next_gc_likely_to_collect_more;
Steve Blocka7e24c12009-10-30 11:49:00 +0000416}
417
418
Steve Blockd0582a62009-12-15 09:54:21 +0000419void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) {
420 // Traversal of global handles marked as NORMAL.
Steve Blocka7e24c12009-10-30 11:49:00 +0000421 for (Node* current = head_; current != NULL; current = current->next()) {
422 if (current->state_ == Node::NORMAL) {
423 v->VisitPointer(&current->object_);
424 }
425 }
426}
427
Steve Blockd0582a62009-12-15 09:54:21 +0000428
429void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
430 for (Node* current = head_; current != NULL; current = current->next()) {
431 if (current->state_ != Node::DESTROYED) {
432 v->VisitPointer(&current->object_);
433 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000434 }
Steve Blockd0582a62009-12-15 09:54:21 +0000435}
436
437
438void GlobalHandles::TearDown() {
439 // Reset all the lists.
Steve Blocka7e24c12009-10-30 11:49:00 +0000440 set_head(NULL);
441 set_first_free(NULL);
Steve Blockd0582a62009-12-15 09:54:21 +0000442 set_first_deallocated(NULL);
443 pool_.Release();
Steve Blocka7e24c12009-10-30 11:49:00 +0000444}
445
446
447int GlobalHandles::number_of_weak_handles_ = 0;
448int GlobalHandles::number_of_global_object_weak_handles_ = 0;
449
450GlobalHandles::Node* GlobalHandles::head_ = NULL;
451GlobalHandles::Node* GlobalHandles::first_free_ = NULL;
Steve Blockd0582a62009-12-15 09:54:21 +0000452GlobalHandles::Node* GlobalHandles::first_deallocated_ = NULL;
453
454void GlobalHandles::RecordStats(HeapStats* stats) {
455 *stats->global_handle_count = 0;
456 *stats->weak_global_handle_count = 0;
457 *stats->pending_global_handle_count = 0;
458 *stats->near_death_global_handle_count = 0;
459 *stats->destroyed_global_handle_count = 0;
460 for (Node* current = head_; current != NULL; current = current->next()) {
461 *stats->global_handle_count += 1;
462 if (current->state_ == Node::WEAK) {
463 *stats->weak_global_handle_count += 1;
464 } else if (current->state_ == Node::PENDING) {
465 *stats->pending_global_handle_count += 1;
466 } else if (current->state_ == Node::NEAR_DEATH) {
467 *stats->near_death_global_handle_count += 1;
468 } else if (current->state_ == Node::DESTROYED) {
469 *stats->destroyed_global_handle_count += 1;
470 }
471 }
472}
Steve Blocka7e24c12009-10-30 11:49:00 +0000473
474#ifdef DEBUG
475
476void GlobalHandles::PrintStats() {
477 int total = 0;
478 int weak = 0;
479 int pending = 0;
480 int near_death = 0;
481 int destroyed = 0;
482
483 for (Node* current = head_; current != NULL; current = current->next()) {
484 total++;
485 if (current->state_ == Node::WEAK) weak++;
486 if (current->state_ == Node::PENDING) pending++;
487 if (current->state_ == Node::NEAR_DEATH) near_death++;
488 if (current->state_ == Node::DESTROYED) destroyed++;
489 }
490
491 PrintF("Global Handle Statistics:\n");
Ben Murdochf87a2032010-10-22 12:50:53 +0100492 PrintF(" allocated memory = %" V8_PTR_PREFIX "dB\n", sizeof(Node) * total);
Steve Blocka7e24c12009-10-30 11:49:00 +0000493 PrintF(" # weak = %d\n", weak);
494 PrintF(" # pending = %d\n", pending);
495 PrintF(" # near_death = %d\n", near_death);
496 PrintF(" # destroyed = %d\n", destroyed);
497 PrintF(" # total = %d\n", total);
498}
499
500void GlobalHandles::Print() {
501 PrintF("Global handles:\n");
502 for (Node* current = head_; current != NULL; current = current->next()) {
Ben Murdochf87a2032010-10-22 12:50:53 +0100503 PrintF(" handle %p to %p (weak=%d)\n",
504 reinterpret_cast<void*>(current->handle().location()),
505 reinterpret_cast<void*>(*current->handle()),
506 current->state_ == Node::WEAK);
Steve Blocka7e24c12009-10-30 11:49:00 +0000507 }
508}
509
510#endif
511
512List<ObjectGroup*>* GlobalHandles::ObjectGroups() {
513 // Lazily initialize the list to avoid startup time static constructors.
514 static List<ObjectGroup*> groups(4);
515 return &groups;
516}
517
518void GlobalHandles::AddGroup(Object*** handles, size_t length) {
519 ObjectGroup* new_entry = new ObjectGroup(length);
520 for (size_t i = 0; i < length; ++i)
521 new_entry->objects_.Add(handles[i]);
522 ObjectGroups()->Add(new_entry);
523}
524
525
526void GlobalHandles::RemoveObjectGroups() {
527 List<ObjectGroup*>* object_groups = ObjectGroups();
528 for (int i = 0; i< object_groups->length(); i++) {
529 delete object_groups->at(i);
530 }
531 object_groups->Clear();
532}
533
Steve Blocka7e24c12009-10-30 11:49:00 +0000534} } // namespace v8::internal