global-handles.cc revision 3ce2e2076e8e3e60cf1810eec160ea2d8557e9e7
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 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 47 explicit Node(Object* object) { 48 Initialize(object); 49 // Initialize link structure. 50 next_ = NULL; 51 } 52 53 ~Node() { 54 if (state_ != DESTROYED) Destroy(); 55#ifdef DEBUG 56 // Zap the values for eager trapping. 57 object_ = NULL; 58 next_ = NULL; 59 parameter_or_next_free_.next_free = NULL; 60#endif 61 } 62 63 void Destroy() { 64 if (state_ == WEAK || IsNearDeath()) { 65 GlobalHandles::number_of_weak_handles_--; 66 if (object_->IsJSGlobalObject()) { 67 GlobalHandles::number_of_global_object_weak_handles_--; 68 } 69 } 70 state_ = DESTROYED; 71 } 72 73 // Accessors for next_. 74 Node* next() { return next_; } 75 void set_next(Node* value) { next_ = value; } 76 Node** next_addr() { return &next_; } 77 78 // Accessors for next free node in the free list. 79 Node* next_free() { 80 ASSERT(state_ == DESTROYED); 81 return parameter_or_next_free_.next_free; 82 } 83 void set_next_free(Node* value) { 84 ASSERT(state_ == DESTROYED); 85 parameter_or_next_free_.next_free = value; 86 } 87 88 // Returns a link from the handle. 89 static Node* FromLocation(Object** location) { 90 ASSERT(OFFSET_OF(Node, object_) == 0); 91 return reinterpret_cast<Node*>(location); 92 } 93 94 // Returns the handle. 95 Handle<Object> handle() { return Handle<Object>(&object_); } 96 97 // Make this handle weak. 98 void MakeWeak(void* parameter, WeakReferenceCallback callback) { 99 LOG(HandleEvent("GlobalHandle::MakeWeak", handle().location())); 100 ASSERT(state_ != DESTROYED); 101 if (state_ != WEAK && !IsNearDeath()) { 102 GlobalHandles::number_of_weak_handles_++; 103 if (object_->IsJSGlobalObject()) { 104 GlobalHandles::number_of_global_object_weak_handles_++; 105 } 106 } 107 state_ = WEAK; 108 set_parameter(parameter); 109 callback_ = callback; 110 } 111 112 void ClearWeakness() { 113 LOG(HandleEvent("GlobalHandle::ClearWeakness", handle().location())); 114 ASSERT(state_ != DESTROYED); 115 if (state_ == WEAK || IsNearDeath()) { 116 GlobalHandles::number_of_weak_handles_--; 117 if (object_->IsJSGlobalObject()) { 118 GlobalHandles::number_of_global_object_weak_handles_--; 119 } 120 } 121 state_ = NORMAL; 122 set_parameter(NULL); 123 } 124 125 bool IsNearDeath() { 126 // Check for PENDING to ensure correct answer when processing callbacks. 127 return state_ == PENDING || state_ == NEAR_DEATH; 128 } 129 130 bool IsWeak() { 131 return state_ == WEAK; 132 } 133 134 // Returns the id for this weak handle. 135 void set_parameter(void* parameter) { 136 ASSERT(state_ != DESTROYED); 137 parameter_or_next_free_.parameter = parameter; 138 } 139 void* parameter() { 140 ASSERT(state_ != DESTROYED); 141 return parameter_or_next_free_.parameter; 142 } 143 144 // Returns the callback for this weak handle. 145 WeakReferenceCallback callback() { return callback_; } 146 147 bool PostGarbageCollectionProcessing() { 148 if (state_ != Node::PENDING) return false; 149 LOG(HandleEvent("GlobalHandle::Processing", handle().location())); 150 void* par = parameter(); 151 state_ = NEAR_DEATH; 152 set_parameter(NULL); 153 // The callback function is resolved as late as possible to preserve old 154 // behavior. 155 WeakReferenceCallback func = callback(); 156 if (func == NULL) return false; 157 158 v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle()); 159 { 160 // Forbid reuse of destroyed nodes as they might be already deallocated. 161 // It's fine though to reuse nodes that were destroyed in weak callback 162 // as those cannot be deallocated until we are back from the callback. 163 set_first_free(NULL); 164 // Leaving V8. 165 VMState state(EXTERNAL); 166 func(object, par); 167 } 168 return true; 169 } 170 171 // Place the handle address first to avoid offset computation. 172 Object* object_; // Storage for object pointer. 173 174 // Transition diagram: 175 // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED } 176 enum State { 177 NORMAL, // Normal global handle. 178 WEAK, // Flagged as weak but not yet finalized. 179 PENDING, // Has been recognized as only reachable by weak handles. 180 NEAR_DEATH, // Callback has informed the handle is near death. 181 DESTROYED 182 }; 183 State state_; 184 185 private: 186 // Handle specific callback. 187 WeakReferenceCallback callback_; 188 // Provided data for callback. In DESTROYED state, this is used for 189 // the free list link. 190 union { 191 void* parameter; 192 Node* next_free; 193 } parameter_or_next_free_; 194 195 // Linkage for the list. 196 Node* next_; 197 198 public: 199 TRACK_MEMORY("GlobalHandles::Node") 200}; 201 202 203Handle<Object> GlobalHandles::Create(Object* value) { 204 Counters::global_handles.Increment(); 205 Node* result; 206 if (first_free() == NULL) { 207 // Allocate a new node. 208 result = new Node(value); 209 result->set_next(head()); 210 set_head(result); 211 } else { 212 // Take the first node in the free list. 213 result = first_free(); 214 set_first_free(result->next_free()); 215 result->Initialize(value); 216 } 217 return result->handle(); 218} 219 220 221void GlobalHandles::Destroy(Object** location) { 222 Counters::global_handles.Decrement(); 223 if (location == NULL) return; 224 Node* node = Node::FromLocation(location); 225 node->Destroy(); 226 // Link the destroyed. 227 node->set_next_free(first_free()); 228 set_first_free(node); 229} 230 231 232void GlobalHandles::MakeWeak(Object** location, void* parameter, 233 WeakReferenceCallback callback) { 234 ASSERT(callback != NULL); 235 Node::FromLocation(location)->MakeWeak(parameter, callback); 236} 237 238 239void GlobalHandles::ClearWeakness(Object** location) { 240 Node::FromLocation(location)->ClearWeakness(); 241} 242 243 244bool GlobalHandles::IsNearDeath(Object** location) { 245 return Node::FromLocation(location)->IsNearDeath(); 246} 247 248 249bool GlobalHandles::IsWeak(Object** location) { 250 return Node::FromLocation(location)->IsWeak(); 251} 252 253 254void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) { 255 // Traversal of GC roots in the global handle list that are marked as 256 // WEAK or PENDING. 257 for (Node* current = head_; current != NULL; current = current->next()) { 258 if (current->state_ == Node::WEAK 259 || current->state_ == Node::PENDING 260 || current->state_ == Node::NEAR_DEATH) { 261 v->VisitPointer(¤t->object_); 262 } 263 } 264} 265 266 267void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f, 268 WeakReferenceCallback callback) { 269 for (Node* current = head_; current != NULL; current = current->next()) { 270 if (current->IsWeak() && current->callback() == callback) { 271 f(current->object_, current->parameter()); 272 } 273 } 274} 275 276 277void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) { 278 for (Node* current = head_; current != NULL; current = current->next()) { 279 if (current->state_ == Node::WEAK) { 280 if (f(¤t->object_)) { 281 current->state_ = Node::PENDING; 282 LOG(HandleEvent("GlobalHandle::Pending", current->handle().location())); 283 } 284 } 285 } 286} 287 288 289int post_gc_processing_count = 0; 290 291void GlobalHandles::PostGarbageCollectionProcessing() { 292 // Process weak global handle callbacks. This must be done after the 293 // GC is completely done, because the callbacks may invoke arbitrary 294 // API functions. 295 // At the same time deallocate all DESTROYED nodes 296 ASSERT(Heap::gc_state() == Heap::NOT_IN_GC); 297 const int initial_post_gc_processing_count = ++post_gc_processing_count; 298 Node** p = &head_; 299 while (*p != NULL) { 300 if ((*p)->PostGarbageCollectionProcessing()) { 301 if (initial_post_gc_processing_count != post_gc_processing_count) { 302 // Weak callback triggered another GC and another round of 303 // PostGarbageCollection processing. The current node might 304 // have been deleted in that round, so we need to bail out (or 305 // restart the processing). 306 break; 307 } 308 } 309 if ((*p)->state_ == Node::DESTROYED) { 310 // Delete the link. 311 Node* node = *p; 312 *p = node->next(); // Update the link. 313 delete node; 314 } else { 315 p = (*p)->next_addr(); 316 } 317 } 318 set_first_free(NULL); 319} 320 321 322void GlobalHandles::IterateRoots(ObjectVisitor* v) { 323 // Traversal of global handles marked as NORMAL or NEAR_DEATH. 324 for (Node* current = head_; current != NULL; current = current->next()) { 325 if (current->state_ == Node::NORMAL) { 326 v->VisitPointer(¤t->object_); 327 } 328 } 329} 330 331void GlobalHandles::TearDown() { 332 // Delete all the nodes in the linked list. 333 Node* current = head_; 334 while (current != NULL) { 335 Node* n = current; 336 current = current->next(); 337 delete n; 338 } 339 // Reset the head and free_list. 340 set_head(NULL); 341 set_first_free(NULL); 342} 343 344 345int GlobalHandles::number_of_weak_handles_ = 0; 346int GlobalHandles::number_of_global_object_weak_handles_ = 0; 347 348GlobalHandles::Node* GlobalHandles::head_ = NULL; 349GlobalHandles::Node* GlobalHandles::first_free_ = NULL; 350 351#ifdef DEBUG 352 353void GlobalHandles::PrintStats() { 354 int total = 0; 355 int weak = 0; 356 int pending = 0; 357 int near_death = 0; 358 int destroyed = 0; 359 360 for (Node* current = head_; current != NULL; current = current->next()) { 361 total++; 362 if (current->state_ == Node::WEAK) weak++; 363 if (current->state_ == Node::PENDING) pending++; 364 if (current->state_ == Node::NEAR_DEATH) near_death++; 365 if (current->state_ == Node::DESTROYED) destroyed++; 366 } 367 368 PrintF("Global Handle Statistics:\n"); 369 PrintF(" allocated memory = %dB\n", sizeof(Node) * total); 370 PrintF(" # weak = %d\n", weak); 371 PrintF(" # pending = %d\n", pending); 372 PrintF(" # near_death = %d\n", near_death); 373 PrintF(" # destroyed = %d\n", destroyed); 374 PrintF(" # total = %d\n", total); 375} 376 377void GlobalHandles::Print() { 378 PrintF("Global handles:\n"); 379 for (Node* current = head_; current != NULL; current = current->next()) { 380 PrintF(" handle %p to %p (weak=%d)\n", current->handle().location(), 381 *current->handle(), current->state_ == Node::WEAK); 382 } 383} 384 385#endif 386 387List<ObjectGroup*>* GlobalHandles::ObjectGroups() { 388 // Lazily initialize the list to avoid startup time static constructors. 389 static List<ObjectGroup*> groups(4); 390 return &groups; 391} 392 393void GlobalHandles::AddGroup(Object*** handles, size_t length) { 394 ObjectGroup* new_entry = new ObjectGroup(length); 395 for (size_t i = 0; i < length; ++i) 396 new_entry->objects_.Add(handles[i]); 397 ObjectGroups()->Add(new_entry); 398} 399 400 401void GlobalHandles::RemoveObjectGroups() { 402 List<ObjectGroup*>* object_groups = ObjectGroups(); 403 for (int i = 0; i< object_groups->length(); i++) { 404 delete object_groups->at(i); 405 } 406 object_groups->Clear(); 407} 408 409 410} } // namespace v8::internal 411