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 namespace v8 { 34 namespace internal { 35 36 class 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 47 Node() { 48 state_ = DESTROYED; 49 } 50 51 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())); 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(); 160 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 if (first_deallocated()) { 169 first_deallocated()->set_next(head()); 170 } 171 // Check that we are not passing a finalized external string to 172 // the callback. 173 ASSERT(!object_->IsExternalAsciiString() || 174 ExternalAsciiString::cast(object_)->resource() != NULL); 175 ASSERT(!object_->IsExternalTwoByteString() || 176 ExternalTwoByteString::cast(object_)->resource() != NULL); 177 // Leaving V8. 178 VMState state(EXTERNAL); 179 func(object, par); 180 } 181 return true; 182 } 183 184 // Place the handle address first to avoid offset computation. 185 Object* object_; // Storage for object pointer. 186 187 // Transition diagram: 188 // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED } 189 enum State { 190 NORMAL, // Normal global handle. 191 WEAK, // Flagged as weak but not yet finalized. 192 PENDING, // Has been recognized as only reachable by weak handles. 193 NEAR_DEATH, // Callback has informed the handle is near death. 194 DESTROYED 195 }; 196 State state_; 197 198 private: 199 // Handle specific callback. 200 WeakReferenceCallback callback_; 201 // Provided data for callback. In DESTROYED state, this is used for 202 // the free list link. 203 union { 204 void* parameter; 205 Node* next_free; 206 } parameter_or_next_free_; 207 208 // Linkage for the list. 209 Node* next_; 210 211 public: 212 TRACK_MEMORY("GlobalHandles::Node") 213 }; 214 215 216 class GlobalHandles::Pool BASE_EMBEDDED { 217 public: 218 Pool() { 219 current_ = new Chunk(); 220 current_->previous = NULL; 221 next_ = current_->nodes; 222 limit_ = current_->nodes + kNodesPerChunk; 223 } 224 225 Node* Allocate() { 226 if (next_ < limit_) { 227 return next_++; 228 } 229 return SlowAllocate(); 230 } 231 232 void Release() { 233 Chunk* current = current_; 234 ASSERT(current != NULL); // At least a single block must by allocated 235 do { 236 Chunk* previous = current->previous; 237 delete current; 238 current = previous; 239 } while (current != NULL); 240 current_ = NULL; 241 next_ = limit_ = NULL; 242 } 243 244 private: 245 static const int kNodesPerChunk = (1 << 12) - 1; 246 struct Chunk : public Malloced { 247 Chunk* previous; 248 Node nodes[kNodesPerChunk]; 249 }; 250 251 Node* SlowAllocate() { 252 Chunk* chunk = new Chunk(); 253 chunk->previous = current_; 254 current_ = chunk; 255 256 Node* new_nodes = current_->nodes; 257 next_ = new_nodes + 1; 258 limit_ = new_nodes + kNodesPerChunk; 259 return new_nodes; 260 } 261 262 Chunk* current_; 263 Node* next_; 264 Node* limit_; 265 }; 266 267 268 static GlobalHandles::Pool pool_; 269 270 271 Handle<Object> GlobalHandles::Create(Object* value) { 272 Counters::global_handles.Increment(); 273 Node* result; 274 if (first_free()) { 275 // Take the first node in the free list. 276 result = first_free(); 277 set_first_free(result->next_free()); 278 } else if (first_deallocated()) { 279 // Next try deallocated list 280 result = first_deallocated(); 281 set_first_deallocated(result->next_free()); 282 ASSERT(result->next() == head()); 283 set_head(result); 284 } else { 285 // Allocate a new node. 286 result = pool_.Allocate(); 287 result->set_next(head()); 288 set_head(result); 289 } 290 result->Initialize(value); 291 return result->handle(); 292 } 293 294 295 void GlobalHandles::Destroy(Object** location) { 296 Counters::global_handles.Decrement(); 297 if (location == NULL) return; 298 Node* node = Node::FromLocation(location); 299 node->Destroy(); 300 // Link the destroyed. 301 node->set_next_free(first_free()); 302 set_first_free(node); 303 } 304 305 306 void GlobalHandles::MakeWeak(Object** location, void* parameter, 307 WeakReferenceCallback callback) { 308 ASSERT(callback != NULL); 309 Node::FromLocation(location)->MakeWeak(parameter, callback); 310 } 311 312 313 void GlobalHandles::ClearWeakness(Object** location) { 314 Node::FromLocation(location)->ClearWeakness(); 315 } 316 317 318 bool GlobalHandles::IsNearDeath(Object** location) { 319 return Node::FromLocation(location)->IsNearDeath(); 320 } 321 322 323 bool GlobalHandles::IsWeak(Object** location) { 324 return Node::FromLocation(location)->IsWeak(); 325 } 326 327 328 void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) { 329 // Traversal of GC roots in the global handle list that are marked as 330 // WEAK or PENDING. 331 for (Node* current = head_; current != NULL; current = current->next()) { 332 if (current->state_ == Node::WEAK 333 || current->state_ == Node::PENDING 334 || current->state_ == Node::NEAR_DEATH) { 335 v->VisitPointer(¤t->object_); 336 } 337 } 338 } 339 340 341 void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f, 342 WeakReferenceCallback callback) { 343 for (Node* current = head_; current != NULL; current = current->next()) { 344 if (current->IsWeak() && current->callback() == callback) { 345 f(current->object_, current->parameter()); 346 } 347 } 348 } 349 350 351 void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) { 352 for (Node* current = head_; current != NULL; current = current->next()) { 353 if (current->state_ == Node::WEAK) { 354 if (f(¤t->object_)) { 355 current->state_ = Node::PENDING; 356 LOG(HandleEvent("GlobalHandle::Pending", current->handle().location())); 357 } 358 } 359 } 360 } 361 362 363 int post_gc_processing_count = 0; 364 365 void GlobalHandles::PostGarbageCollectionProcessing() { 366 // Process weak global handle callbacks. This must be done after the 367 // GC is completely done, because the callbacks may invoke arbitrary 368 // API functions. 369 // At the same time deallocate all DESTROYED nodes. 370 ASSERT(Heap::gc_state() == Heap::NOT_IN_GC); 371 const int initial_post_gc_processing_count = ++post_gc_processing_count; 372 Node** p = &head_; 373 while (*p != NULL) { 374 if ((*p)->PostGarbageCollectionProcessing()) { 375 if (initial_post_gc_processing_count != post_gc_processing_count) { 376 // Weak callback triggered another GC and another round of 377 // PostGarbageCollection processing. The current node might 378 // have been deleted in that round, so we need to bail out (or 379 // restart the processing). 380 break; 381 } 382 } 383 if ((*p)->state_ == Node::DESTROYED) { 384 // Delete the link. 385 Node* node = *p; 386 *p = node->next(); // Update the link. 387 if (first_deallocated()) { 388 first_deallocated()->set_next(node); 389 } 390 node->set_next_free(first_deallocated()); 391 set_first_deallocated(node); 392 } else { 393 p = (*p)->next_addr(); 394 } 395 } 396 set_first_free(NULL); 397 if (first_deallocated()) { 398 first_deallocated()->set_next(head()); 399 } 400 } 401 402 403 void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) { 404 // Traversal of global handles marked as NORMAL. 405 for (Node* current = head_; current != NULL; current = current->next()) { 406 if (current->state_ == Node::NORMAL) { 407 v->VisitPointer(¤t->object_); 408 } 409 } 410 } 411 412 413 void GlobalHandles::IterateAllRoots(ObjectVisitor* v) { 414 for (Node* current = head_; current != NULL; current = current->next()) { 415 if (current->state_ != Node::DESTROYED) { 416 v->VisitPointer(¤t->object_); 417 } 418 } 419 } 420 421 422 void GlobalHandles::TearDown() { 423 // Reset all the lists. 424 set_head(NULL); 425 set_first_free(NULL); 426 set_first_deallocated(NULL); 427 pool_.Release(); 428 } 429 430 431 int GlobalHandles::number_of_weak_handles_ = 0; 432 int GlobalHandles::number_of_global_object_weak_handles_ = 0; 433 434 GlobalHandles::Node* GlobalHandles::head_ = NULL; 435 GlobalHandles::Node* GlobalHandles::first_free_ = NULL; 436 GlobalHandles::Node* GlobalHandles::first_deallocated_ = NULL; 437 438 void GlobalHandles::RecordStats(HeapStats* stats) { 439 *stats->global_handle_count = 0; 440 *stats->weak_global_handle_count = 0; 441 *stats->pending_global_handle_count = 0; 442 *stats->near_death_global_handle_count = 0; 443 *stats->destroyed_global_handle_count = 0; 444 for (Node* current = head_; current != NULL; current = current->next()) { 445 *stats->global_handle_count += 1; 446 if (current->state_ == Node::WEAK) { 447 *stats->weak_global_handle_count += 1; 448 } else if (current->state_ == Node::PENDING) { 449 *stats->pending_global_handle_count += 1; 450 } else if (current->state_ == Node::NEAR_DEATH) { 451 *stats->near_death_global_handle_count += 1; 452 } else if (current->state_ == Node::DESTROYED) { 453 *stats->destroyed_global_handle_count += 1; 454 } 455 } 456 } 457 458 #ifdef DEBUG 459 460 void GlobalHandles::PrintStats() { 461 int total = 0; 462 int weak = 0; 463 int pending = 0; 464 int near_death = 0; 465 int destroyed = 0; 466 467 for (Node* current = head_; current != NULL; current = current->next()) { 468 total++; 469 if (current->state_ == Node::WEAK) weak++; 470 if (current->state_ == Node::PENDING) pending++; 471 if (current->state_ == Node::NEAR_DEATH) near_death++; 472 if (current->state_ == Node::DESTROYED) destroyed++; 473 } 474 475 PrintF("Global Handle Statistics:\n"); 476 PrintF(" allocated memory = %dB\n", sizeof(Node) * total); 477 PrintF(" # weak = %d\n", weak); 478 PrintF(" # pending = %d\n", pending); 479 PrintF(" # near_death = %d\n", near_death); 480 PrintF(" # destroyed = %d\n", destroyed); 481 PrintF(" # total = %d\n", total); 482 } 483 484 void GlobalHandles::Print() { 485 PrintF("Global handles:\n"); 486 for (Node* current = head_; current != NULL; current = current->next()) { 487 PrintF(" handle %p to %p (weak=%d)\n", current->handle().location(), 488 *current->handle(), current->state_ == Node::WEAK); 489 } 490 } 491 492 #endif 493 494 List<ObjectGroup*>* GlobalHandles::ObjectGroups() { 495 // Lazily initialize the list to avoid startup time static constructors. 496 static List<ObjectGroup*> groups(4); 497 return &groups; 498 } 499 500 void GlobalHandles::AddGroup(Object*** handles, size_t length) { 501 ObjectGroup* new_entry = new ObjectGroup(length); 502 for (size_t i = 0; i < length; ++i) 503 new_entry->objects_.Add(handles[i]); 504 ObjectGroups()->Add(new_entry); 505 } 506 507 508 void GlobalHandles::RemoveObjectGroups() { 509 List<ObjectGroup*>* object_groups = ObjectGroups(); 510 for (int i = 0; i< object_groups->length(); i++) { 511 delete object_groups->at(i); 512 } 513 object_groups->Clear(); 514 } 515 516 } } // namespace v8::internal 517