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 39 ObjectGroup::~ObjectGroup() { 40 if (info_ != NULL) info_->Dispose(); 41 } 42 43 44 class GlobalHandles::Node : public Malloced { 45 public: 46 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 56 Node() { 57 state_ = DESTROYED; 58 } 59 60 explicit Node(Object* object) { 61 Initialize(object); 62 // Initialize link structure. 63 next_ = NULL; 64 } 65 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 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_. 87 Node* next() { return next_; } 88 void set_next(Node* value) { next_ = value; } 89 Node** next_addr() { return &next_; } 90 91 // Accessors for next free node in the free list. 92 Node* next_free() { 93 ASSERT(state_ == DESTROYED); 94 return parameter_or_next_free_.next_free; 95 } 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. 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. 108 Handle<Object> handle() { return Handle<Object>(&object_); } 109 110 // Make this handle weak. 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 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 141 bool IsNearDeath() { 142 // Check for PENDING to ensure correct answer when processing callbacks. 143 return state_ == PENDING || state_ == NEAR_DEATH; 144 } 145 146 bool IsWeak() { 147 return state_ == WEAK; 148 } 149 150 bool CanBeRetainer() { 151 return state_ != DESTROYED && state_ != NEAR_DEATH; 152 } 153 154 void SetWrapperClassId(uint16_t class_id) { 155 class_id_ = class_id; 156 } 157 158 // Returns the id for this weak handle. 159 void set_parameter(void* parameter) { 160 ASSERT(state_ != DESTROYED); 161 parameter_or_next_free_.parameter = parameter; 162 } 163 void* parameter() { 164 ASSERT(state_ != DESTROYED); 165 return parameter_or_next_free_.parameter; 166 } 167 168 // Returns the callback for this weak handle. 169 WeakReferenceCallback callback() { return callback_; } 170 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: 245 Pool() { 246 current_ = new Chunk(); 247 current_->previous = NULL; 248 next_ = current_->nodes; 249 limit_ = current_->nodes + kNodesPerChunk; 250 } 251 252 ~Pool() { 253 if (current_ != NULL) { 254 Release(); 255 } 256 } 257 258 Node* Allocate() { 259 if (next_ < limit_) { 260 return next_++; 261 } 262 return SlowAllocate(); 263 } 264 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 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 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 314 GlobalHandles::~GlobalHandles() { 315 delete pool_; 316 pool_ = 0; 317 } 318 319 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 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 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 362 void GlobalHandles::ClearWeakness(Object** location) { 363 Node::FromLocation(location)->ClearWeakness(this); 364 } 365 366 367 bool GlobalHandles::IsNearDeath(Object** location) { 368 return Node::FromLocation(location)->IsNearDeath(); 369 } 370 371 372 bool GlobalHandles::IsWeak(Object** location) { 373 return Node::FromLocation(location)->IsWeak(); 374 } 375 376 377 void GlobalHandles::SetWrapperClassId(Object** location, uint16_t class_id) { 378 Node::FromLocation(location)->SetWrapperClassId(class_id); 379 } 380 381 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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