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