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
Initialize(Object * object)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
Node()47 Node() {
48 state_ = DESTROYED;
49 }
50
Node(Object * object)51 explicit Node(Object* object) {
52 Initialize(object);
53 // Initialize link structure.
54 next_ = NULL;
55 }
56
~Node()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
Destroy()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_.
next()78 Node* next() { return next_; }
set_next(Node * value)79 void set_next(Node* value) { next_ = value; }
next_addr()80 Node** next_addr() { return &next_; }
81
82 // Accessors for next free node in the free list.
next_free()83 Node* next_free() {
84 ASSERT(state_ == DESTROYED);
85 return parameter_or_next_free_.next_free;
86 }
set_next_free(Node * value)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.
FromLocation(Object ** location)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.
handle()99 Handle<Object> handle() { return Handle<Object>(&object_); }
100
101 // Make this handle weak.
MakeWeak(void * parameter,WeakReferenceCallback callback)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
ClearWeakness()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
IsNearDeath()129 bool IsNearDeath() {
130 // Check for PENDING to ensure correct answer when processing callbacks.
131 return state_ == PENDING || state_ == NEAR_DEATH;
132 }
133
IsWeak()134 bool IsWeak() {
135 return state_ == WEAK;
136 }
137
138 // Returns the id for this weak handle.
set_parameter(void * parameter)139 void set_parameter(void* parameter) {
140 ASSERT(state_ != DESTROYED);
141 parameter_or_next_free_.parameter = parameter;
142 }
parameter()143 void* parameter() {
144 ASSERT(state_ != DESTROYED);
145 return parameter_or_next_free_.parameter;
146 }
147
148 // Returns the callback for this weak handle.
callback()149 WeakReferenceCallback callback() { return callback_; }
150
PostGarbageCollectionProcessing()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:
Pool()218 Pool() {
219 current_ = new Chunk();
220 current_->previous = NULL;
221 next_ = current_->nodes;
222 limit_ = current_->nodes + kNodesPerChunk;
223 }
224
Allocate()225 Node* Allocate() {
226 if (next_ < limit_) {
227 return next_++;
228 }
229 return SlowAllocate();
230 }
231
Release()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
SlowAllocate()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
Create(Object * value)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
Destroy(Object ** location)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
MakeWeak(Object ** location,void * parameter,WeakReferenceCallback callback)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
ClearWeakness(Object ** location)313 void GlobalHandles::ClearWeakness(Object** location) {
314 Node::FromLocation(location)->ClearWeakness();
315 }
316
317
IsNearDeath(Object ** location)318 bool GlobalHandles::IsNearDeath(Object** location) {
319 return Node::FromLocation(location)->IsNearDeath();
320 }
321
322
IsWeak(Object ** location)323 bool GlobalHandles::IsWeak(Object** location) {
324 return Node::FromLocation(location)->IsWeak();
325 }
326
327
IterateWeakRoots(ObjectVisitor * v)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
IterateWeakRoots(WeakReferenceGuest f,WeakReferenceCallback callback)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
IdentifyWeakHandles(WeakSlotCallback f)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
PostGarbageCollectionProcessing()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
IterateStrongRoots(ObjectVisitor * v)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
IterateAllRoots(ObjectVisitor * v)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
TearDown()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
RecordStats(HeapStats * stats)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
PrintStats()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
Print()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
ObjectGroups()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
AddGroup(Object *** handles,size_t length)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
RemoveObjectGroups()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