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 #include "vm-state-inl.h"
34
35 namespace v8 {
36 namespace internal {
37
38
~ObjectGroup()39 ObjectGroup::~ObjectGroup() {
40 if (info_ != NULL) info_->Dispose();
41 }
42
43
44 class GlobalHandles::Node : public Malloced {
45 public:
46
Initialize(Object * object)47 void Initialize(Object* object) {
48 // Set the initial value of the handle.
49 object_ = object;
50 class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
51 state_ = NORMAL;
52 parameter_or_next_free_.parameter = NULL;
53 callback_ = NULL;
54 }
55
Node()56 Node() {
57 state_ = DESTROYED;
58 }
59
Node(Object * object)60 explicit Node(Object* object) {
61 Initialize(object);
62 // Initialize link structure.
63 next_ = NULL;
64 }
65
~Node()66 ~Node() {
67 if (state_ != DESTROYED) Destroy(Isolate::Current()->global_handles());
68 #ifdef DEBUG
69 // Zap the values for eager trapping.
70 object_ = NULL;
71 next_ = NULL;
72 parameter_or_next_free_.next_free = NULL;
73 #endif
74 }
75
Destroy(GlobalHandles * global_handles)76 void Destroy(GlobalHandles* global_handles) {
77 if (state_ == WEAK || IsNearDeath()) {
78 global_handles->number_of_weak_handles_--;
79 if (object_->IsJSGlobalObject()) {
80 global_handles->number_of_global_object_weak_handles_--;
81 }
82 }
83 state_ = DESTROYED;
84 }
85
86 // Accessors for next_.
next()87 Node* next() { return next_; }
set_next(Node * value)88 void set_next(Node* value) { next_ = value; }
next_addr()89 Node** next_addr() { return &next_; }
90
91 // Accessors for next free node in the free list.
next_free()92 Node* next_free() {
93 ASSERT(state_ == DESTROYED);
94 return parameter_or_next_free_.next_free;
95 }
set_next_free(Node * value)96 void set_next_free(Node* value) {
97 ASSERT(state_ == DESTROYED);
98 parameter_or_next_free_.next_free = value;
99 }
100
101 // Returns a link from the handle.
FromLocation(Object ** location)102 static Node* FromLocation(Object** location) {
103 ASSERT(OFFSET_OF(Node, object_) == 0);
104 return reinterpret_cast<Node*>(location);
105 }
106
107 // Returns the handle.
handle()108 Handle<Object> handle() { return Handle<Object>(&object_); }
109
110 // Make this handle weak.
MakeWeak(GlobalHandles * global_handles,void * parameter,WeakReferenceCallback callback)111 void MakeWeak(GlobalHandles* global_handles, void* parameter,
112 WeakReferenceCallback callback) {
113 LOG(global_handles->isolate(),
114 HandleEvent("GlobalHandle::MakeWeak", handle().location()));
115 ASSERT(state_ != DESTROYED);
116 if (state_ != WEAK && !IsNearDeath()) {
117 global_handles->number_of_weak_handles_++;
118 if (object_->IsJSGlobalObject()) {
119 global_handles->number_of_global_object_weak_handles_++;
120 }
121 }
122 state_ = WEAK;
123 set_parameter(parameter);
124 callback_ = callback;
125 }
126
ClearWeakness(GlobalHandles * global_handles)127 void ClearWeakness(GlobalHandles* global_handles) {
128 LOG(global_handles->isolate(),
129 HandleEvent("GlobalHandle::ClearWeakness", handle().location()));
130 ASSERT(state_ != DESTROYED);
131 if (state_ == WEAK || IsNearDeath()) {
132 global_handles->number_of_weak_handles_--;
133 if (object_->IsJSGlobalObject()) {
134 global_handles->number_of_global_object_weak_handles_--;
135 }
136 }
137 state_ = NORMAL;
138 set_parameter(NULL);
139 }
140
IsNearDeath()141 bool IsNearDeath() {
142 // Check for PENDING to ensure correct answer when processing callbacks.
143 return state_ == PENDING || state_ == NEAR_DEATH;
144 }
145
IsWeak()146 bool IsWeak() {
147 return state_ == WEAK;
148 }
149
CanBeRetainer()150 bool CanBeRetainer() {
151 return state_ != DESTROYED && state_ != NEAR_DEATH;
152 }
153
SetWrapperClassId(uint16_t class_id)154 void SetWrapperClassId(uint16_t class_id) {
155 class_id_ = class_id;
156 }
157
158 // Returns the id for this weak handle.
set_parameter(void * parameter)159 void set_parameter(void* parameter) {
160 ASSERT(state_ != DESTROYED);
161 parameter_or_next_free_.parameter = parameter;
162 }
parameter()163 void* parameter() {
164 ASSERT(state_ != DESTROYED);
165 return parameter_or_next_free_.parameter;
166 }
167
168 // Returns the callback for this weak handle.
callback()169 WeakReferenceCallback callback() { return callback_; }
170
PostGarbageCollectionProcessing(Isolate * isolate,GlobalHandles * global_handles)171 bool PostGarbageCollectionProcessing(Isolate* isolate,
172 GlobalHandles* global_handles) {
173 if (state_ != Node::PENDING) return false;
174 LOG(isolate, HandleEvent("GlobalHandle::Processing", handle().location()));
175 WeakReferenceCallback func = callback();
176 if (func == NULL) {
177 Destroy(global_handles);
178 return false;
179 }
180 void* par = parameter();
181 state_ = NEAR_DEATH;
182 set_parameter(NULL);
183
184 v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle());
185 {
186 // Forbid reuse of destroyed nodes as they might be already deallocated.
187 // It's fine though to reuse nodes that were destroyed in weak callback
188 // as those cannot be deallocated until we are back from the callback.
189 global_handles->set_first_free(NULL);
190 if (global_handles->first_deallocated()) {
191 global_handles->first_deallocated()->set_next(global_handles->head());
192 }
193 // Check that we are not passing a finalized external string to
194 // the callback.
195 ASSERT(!object_->IsExternalAsciiString() ||
196 ExternalAsciiString::cast(object_)->resource() != NULL);
197 ASSERT(!object_->IsExternalTwoByteString() ||
198 ExternalTwoByteString::cast(object_)->resource() != NULL);
199 // Leaving V8.
200 VMState state(isolate, EXTERNAL);
201 func(object, par);
202 }
203 // Absense of explicit cleanup or revival of weak handle
204 // in most of the cases would lead to memory leak.
205 ASSERT(state_ != NEAR_DEATH);
206 return true;
207 }
208
209 // Place the handle address first to avoid offset computation.
210 Object* object_; // Storage for object pointer.
211
212 uint16_t class_id_;
213
214 // Transition diagram:
215 // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
216 enum State {
217 NORMAL, // Normal global handle.
218 WEAK, // Flagged as weak but not yet finalized.
219 PENDING, // Has been recognized as only reachable by weak handles.
220 NEAR_DEATH, // Callback has informed the handle is near death.
221 DESTROYED
222 };
223 State state_ : 4; // Need one more bit for MSVC as it treats enums as signed.
224
225 private:
226 // Handle specific callback.
227 WeakReferenceCallback callback_;
228 // Provided data for callback. In DESTROYED state, this is used for
229 // the free list link.
230 union {
231 void* parameter;
232 Node* next_free;
233 } parameter_or_next_free_;
234
235 // Linkage for the list.
236 Node* next_;
237
238 public:
239 TRACK_MEMORY("GlobalHandles::Node")
240 };
241
242
243 class GlobalHandles::Pool {
244 public:
Pool()245 Pool() {
246 current_ = new Chunk();
247 current_->previous = NULL;
248 next_ = current_->nodes;
249 limit_ = current_->nodes + kNodesPerChunk;
250 }
251
~Pool()252 ~Pool() {
253 if (current_ != NULL) {
254 Release();
255 }
256 }
257
Allocate()258 Node* Allocate() {
259 if (next_ < limit_) {
260 return next_++;
261 }
262 return SlowAllocate();
263 }
264
Release()265 void Release() {
266 Chunk* current = current_;
267 ASSERT(current != NULL); // At least a single block must by allocated
268 do {
269 Chunk* previous = current->previous;
270 delete current;
271 current = previous;
272 } while (current != NULL);
273 current_ = NULL;
274 next_ = limit_ = NULL;
275 }
276
277 private:
278 static const int kNodesPerChunk = (1 << 12) - 1;
279 struct Chunk : public Malloced {
280 Chunk* previous;
281 Node nodes[kNodesPerChunk];
282 };
283
SlowAllocate()284 Node* SlowAllocate() {
285 Chunk* chunk = new Chunk();
286 chunk->previous = current_;
287 current_ = chunk;
288
289 Node* new_nodes = current_->nodes;
290 next_ = new_nodes + 1;
291 limit_ = new_nodes + kNodesPerChunk;
292 return new_nodes;
293 }
294
295 Chunk* current_;
296 Node* next_;
297 Node* limit_;
298 };
299
300
GlobalHandles(Isolate * isolate)301 GlobalHandles::GlobalHandles(Isolate* isolate)
302 : isolate_(isolate),
303 number_of_weak_handles_(0),
304 number_of_global_object_weak_handles_(0),
305 head_(NULL),
306 first_free_(NULL),
307 first_deallocated_(NULL),
308 pool_(new Pool()),
309 post_gc_processing_count_(0),
310 object_groups_(4) {
311 }
312
313
~GlobalHandles()314 GlobalHandles::~GlobalHandles() {
315 delete pool_;
316 pool_ = 0;
317 }
318
319
Create(Object * value)320 Handle<Object> GlobalHandles::Create(Object* value) {
321 isolate_->counters()->global_handles()->Increment();
322 Node* result;
323 if (first_free()) {
324 // Take the first node in the free list.
325 result = first_free();
326 set_first_free(result->next_free());
327 } else if (first_deallocated()) {
328 // Next try deallocated list
329 result = first_deallocated();
330 set_first_deallocated(result->next_free());
331 ASSERT(result->next() == head());
332 set_head(result);
333 } else {
334 // Allocate a new node.
335 result = pool_->Allocate();
336 result->set_next(head());
337 set_head(result);
338 }
339 result->Initialize(value);
340 return result->handle();
341 }
342
343
Destroy(Object ** location)344 void GlobalHandles::Destroy(Object** location) {
345 isolate_->counters()->global_handles()->Decrement();
346 if (location == NULL) return;
347 Node* node = Node::FromLocation(location);
348 node->Destroy(this);
349 // Link the destroyed.
350 node->set_next_free(first_free());
351 set_first_free(node);
352 }
353
354
MakeWeak(Object ** location,void * parameter,WeakReferenceCallback callback)355 void GlobalHandles::MakeWeak(Object** location, void* parameter,
356 WeakReferenceCallback callback) {
357 ASSERT(callback != NULL);
358 Node::FromLocation(location)->MakeWeak(this, parameter, callback);
359 }
360
361
ClearWeakness(Object ** location)362 void GlobalHandles::ClearWeakness(Object** location) {
363 Node::FromLocation(location)->ClearWeakness(this);
364 }
365
366
IsNearDeath(Object ** location)367 bool GlobalHandles::IsNearDeath(Object** location) {
368 return Node::FromLocation(location)->IsNearDeath();
369 }
370
371
IsWeak(Object ** location)372 bool GlobalHandles::IsWeak(Object** location) {
373 return Node::FromLocation(location)->IsWeak();
374 }
375
376
SetWrapperClassId(Object ** location,uint16_t class_id)377 void GlobalHandles::SetWrapperClassId(Object** location, uint16_t class_id) {
378 Node::FromLocation(location)->SetWrapperClassId(class_id);
379 }
380
381
IterateWeakRoots(ObjectVisitor * v)382 void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
383 // Traversal of GC roots in the global handle list that are marked as
384 // WEAK or PENDING.
385 for (Node* current = head_; current != NULL; current = current->next()) {
386 if (current->state_ == Node::WEAK
387 || current->state_ == Node::PENDING
388 || current->state_ == Node::NEAR_DEATH) {
389 v->VisitPointer(¤t->object_);
390 }
391 }
392 }
393
394
IterateWeakRoots(WeakReferenceGuest f,WeakReferenceCallback callback)395 void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f,
396 WeakReferenceCallback callback) {
397 for (Node* current = head_; current != NULL; current = current->next()) {
398 if (current->IsWeak() && current->callback() == callback) {
399 f(current->object_, current->parameter());
400 }
401 }
402 }
403
404
IdentifyWeakHandles(WeakSlotCallback f)405 void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
406 for (Node* current = head_; current != NULL; current = current->next()) {
407 if (current->state_ == Node::WEAK) {
408 if (f(¤t->object_)) {
409 current->state_ = Node::PENDING;
410 LOG(isolate_,
411 HandleEvent("GlobalHandle::Pending", current->handle().location()));
412 }
413 }
414 }
415 }
416
417
PostGarbageCollectionProcessing()418 bool GlobalHandles::PostGarbageCollectionProcessing() {
419 // Process weak global handle callbacks. This must be done after the
420 // GC is completely done, because the callbacks may invoke arbitrary
421 // API functions.
422 // At the same time deallocate all DESTROYED nodes.
423 ASSERT(isolate_->heap()->gc_state() == Heap::NOT_IN_GC);
424 const int initial_post_gc_processing_count = ++post_gc_processing_count_;
425 bool next_gc_likely_to_collect_more = false;
426 Node** p = &head_;
427 while (*p != NULL) {
428 if ((*p)->PostGarbageCollectionProcessing(isolate_, this)) {
429 if (initial_post_gc_processing_count != post_gc_processing_count_) {
430 // Weak callback triggered another GC and another round of
431 // PostGarbageCollection processing. The current node might
432 // have been deleted in that round, so we need to bail out (or
433 // restart the processing).
434 break;
435 }
436 }
437 if ((*p)->state_ == Node::DESTROYED) {
438 // Delete the link.
439 Node* node = *p;
440 *p = node->next(); // Update the link.
441 if (first_deallocated()) {
442 first_deallocated()->set_next(node);
443 }
444 node->set_next_free(first_deallocated());
445 set_first_deallocated(node);
446 next_gc_likely_to_collect_more = true;
447 } else {
448 p = (*p)->next_addr();
449 }
450 }
451 set_first_free(NULL);
452 if (first_deallocated()) {
453 first_deallocated()->set_next(head());
454 }
455
456 return next_gc_likely_to_collect_more;
457 }
458
459
IterateStrongRoots(ObjectVisitor * v)460 void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) {
461 // Traversal of global handles marked as NORMAL.
462 for (Node* current = head_; current != NULL; current = current->next()) {
463 if (current->state_ == Node::NORMAL) {
464 v->VisitPointer(¤t->object_);
465 }
466 }
467 }
468
469
IterateAllRoots(ObjectVisitor * v)470 void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
471 for (Node* current = head_; current != NULL; current = current->next()) {
472 if (current->state_ != Node::DESTROYED) {
473 v->VisitPointer(¤t->object_);
474 }
475 }
476 }
477
478
IterateAllRootsWithClassIds(ObjectVisitor * v)479 void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) {
480 for (Node* current = head_; current != NULL; current = current->next()) {
481 if (current->class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId &&
482 current->CanBeRetainer()) {
483 v->VisitEmbedderReference(¤t->object_, current->class_id_);
484 }
485 }
486 }
487
488
TearDown()489 void GlobalHandles::TearDown() {
490 // Reset all the lists.
491 set_head(NULL);
492 set_first_free(NULL);
493 set_first_deallocated(NULL);
494 pool_->Release();
495 }
496
497
RecordStats(HeapStats * stats)498 void GlobalHandles::RecordStats(HeapStats* stats) {
499 *stats->global_handle_count = 0;
500 *stats->weak_global_handle_count = 0;
501 *stats->pending_global_handle_count = 0;
502 *stats->near_death_global_handle_count = 0;
503 *stats->destroyed_global_handle_count = 0;
504 for (Node* current = head_; current != NULL; current = current->next()) {
505 *stats->global_handle_count += 1;
506 if (current->state_ == Node::WEAK) {
507 *stats->weak_global_handle_count += 1;
508 } else if (current->state_ == Node::PENDING) {
509 *stats->pending_global_handle_count += 1;
510 } else if (current->state_ == Node::NEAR_DEATH) {
511 *stats->near_death_global_handle_count += 1;
512 } else if (current->state_ == Node::DESTROYED) {
513 *stats->destroyed_global_handle_count += 1;
514 }
515 }
516 }
517
518 #ifdef DEBUG
519
PrintStats()520 void GlobalHandles::PrintStats() {
521 int total = 0;
522 int weak = 0;
523 int pending = 0;
524 int near_death = 0;
525 int destroyed = 0;
526
527 for (Node* current = head_; current != NULL; current = current->next()) {
528 total++;
529 if (current->state_ == Node::WEAK) weak++;
530 if (current->state_ == Node::PENDING) pending++;
531 if (current->state_ == Node::NEAR_DEATH) near_death++;
532 if (current->state_ == Node::DESTROYED) destroyed++;
533 }
534
535 PrintF("Global Handle Statistics:\n");
536 PrintF(" allocated memory = %" V8_PTR_PREFIX "dB\n", sizeof(Node) * total);
537 PrintF(" # weak = %d\n", weak);
538 PrintF(" # pending = %d\n", pending);
539 PrintF(" # near_death = %d\n", near_death);
540 PrintF(" # destroyed = %d\n", destroyed);
541 PrintF(" # total = %d\n", total);
542 }
543
Print()544 void GlobalHandles::Print() {
545 PrintF("Global handles:\n");
546 for (Node* current = head_; current != NULL; current = current->next()) {
547 PrintF(" handle %p to %p (weak=%d)\n",
548 reinterpret_cast<void*>(current->handle().location()),
549 reinterpret_cast<void*>(*current->handle()),
550 current->state_ == Node::WEAK);
551 }
552 }
553
554 #endif
555
556
557
AddObjectGroup(Object *** handles,size_t length,v8::RetainedObjectInfo * info)558 void GlobalHandles::AddObjectGroup(Object*** handles,
559 size_t length,
560 v8::RetainedObjectInfo* info) {
561 if (length == 0) {
562 if (info != NULL) info->Dispose();
563 return;
564 }
565 object_groups_.Add(ObjectGroup::New(handles, length, info));
566 }
567
568
AddImplicitReferences(HeapObject ** parent,Object *** children,size_t length)569 void GlobalHandles::AddImplicitReferences(HeapObject** parent,
570 Object*** children,
571 size_t length) {
572 if (length == 0) return;
573 implicit_ref_groups_.Add(ImplicitRefGroup::New(parent, children, length));
574 }
575
576
RemoveObjectGroups()577 void GlobalHandles::RemoveObjectGroups() {
578 for (int i = 0; i < object_groups_.length(); i++) {
579 object_groups_.at(i)->Dispose();
580 }
581 object_groups_.Clear();
582 }
583
584
RemoveImplicitRefGroups()585 void GlobalHandles::RemoveImplicitRefGroups() {
586 for (int i = 0; i < implicit_ref_groups_.length(); i++) {
587 implicit_ref_groups_.at(i)->Dispose();
588 }
589 implicit_ref_groups_.Clear();
590 }
591
592
593 } } // namespace v8::internal
594