blob: e4bb925f0e9299ea105df757004d0912dad893b0 [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 }
Leon Clarkee46be812010-01-19 14:06:41 +0000171 // 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);
Steve Blocka7e24c12009-10-30 11:49:00 +0000177 // 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
Steve Blockd0582a62009-12-15 09:54:21 +0000216class 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
268static GlobalHandles::Pool pool_;
269
270
Steve Blocka7e24c12009-10-30 11:49:00 +0000271Handle<Object> GlobalHandles::Create(Object* value) {
272 Counters::global_handles.Increment();
273 Node* result;
Steve Blockd0582a62009-12-15 09:54:21 +0000274 if (first_free()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000275 // Take the first node in the free list.
276 result = first_free();
277 set_first_free(result->next_free());
Steve Blockd0582a62009-12-15 09:54:21 +0000278 } 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);
Steve Blocka7e24c12009-10-30 11:49:00 +0000289 }
Steve Blockd0582a62009-12-15 09:54:21 +0000290 result->Initialize(value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000291 return result->handle();
292}
293
294
295void 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
306void GlobalHandles::MakeWeak(Object** location, void* parameter,
307 WeakReferenceCallback callback) {
308 ASSERT(callback != NULL);
309 Node::FromLocation(location)->MakeWeak(parameter, callback);
310}
311
312
313void GlobalHandles::ClearWeakness(Object** location) {
314 Node::FromLocation(location)->ClearWeakness();
315}
316
317
318bool GlobalHandles::IsNearDeath(Object** location) {
319 return Node::FromLocation(location)->IsNearDeath();
320}
321
322
323bool GlobalHandles::IsWeak(Object** location) {
324 return Node::FromLocation(location)->IsWeak();
325}
326
327
328void 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(&current->object_);
336 }
337 }
338}
339
340
Steve Block3ce2e202009-11-05 08:53:23 +0000341void 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
Steve Blocka7e24c12009-10-30 11:49:00 +0000351void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
352 for (Node* current = head_; current != NULL; current = current->next()) {
353 if (current->state_ == Node::WEAK) {
354 if (f(&current->object_)) {
355 current->state_ = Node::PENDING;
356 LOG(HandleEvent("GlobalHandle::Pending", current->handle().location()));
357 }
358 }
359 }
360}
361
362
363int post_gc_processing_count = 0;
364
365void 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.
Steve Blockd0582a62009-12-15 09:54:21 +0000369 // At the same time deallocate all DESTROYED nodes.
Steve Blocka7e24c12009-10-30 11:49:00 +0000370 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.
Steve Blockd0582a62009-12-15 09:54:21 +0000387 if (first_deallocated()) {
388 first_deallocated()->set_next(node);
389 }
390 node->set_next_free(first_deallocated());
391 set_first_deallocated(node);
Steve Blocka7e24c12009-10-30 11:49:00 +0000392 } else {
393 p = (*p)->next_addr();
394 }
395 }
396 set_first_free(NULL);
Steve Blockd0582a62009-12-15 09:54:21 +0000397 if (first_deallocated()) {
398 first_deallocated()->set_next(head());
399 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000400}
401
402
Steve Blockd0582a62009-12-15 09:54:21 +0000403void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) {
404 // Traversal of global handles marked as NORMAL.
Steve Blocka7e24c12009-10-30 11:49:00 +0000405 for (Node* current = head_; current != NULL; current = current->next()) {
406 if (current->state_ == Node::NORMAL) {
407 v->VisitPointer(&current->object_);
408 }
409 }
410}
411
Steve Blockd0582a62009-12-15 09:54:21 +0000412
413void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
414 for (Node* current = head_; current != NULL; current = current->next()) {
415 if (current->state_ != Node::DESTROYED) {
416 v->VisitPointer(&current->object_);
417 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000418 }
Steve Blockd0582a62009-12-15 09:54:21 +0000419}
420
421
422void GlobalHandles::TearDown() {
423 // Reset all the lists.
Steve Blocka7e24c12009-10-30 11:49:00 +0000424 set_head(NULL);
425 set_first_free(NULL);
Steve Blockd0582a62009-12-15 09:54:21 +0000426 set_first_deallocated(NULL);
427 pool_.Release();
Steve Blocka7e24c12009-10-30 11:49:00 +0000428}
429
430
431int GlobalHandles::number_of_weak_handles_ = 0;
432int GlobalHandles::number_of_global_object_weak_handles_ = 0;
433
434GlobalHandles::Node* GlobalHandles::head_ = NULL;
435GlobalHandles::Node* GlobalHandles::first_free_ = NULL;
Steve Blockd0582a62009-12-15 09:54:21 +0000436GlobalHandles::Node* GlobalHandles::first_deallocated_ = NULL;
437
438void 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}
Steve Blocka7e24c12009-10-30 11:49:00 +0000457
458#ifdef DEBUG
459
460void 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
484void 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
494List<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
500void 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
508void 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
Steve Blocka7e24c12009-10-30 11:49:00 +0000516} } // namespace v8::internal