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