blob: ed4e262e628856e5a5d16ffd47d0431afaac267c [file] [log] [blame]
ager@chromium.org71daaf62009-04-01 07:22:49 +00001// Copyright 2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// 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
kasperl@chromium.org71affb52009-05-26 05:44:31 +000033namespace v8 {
34namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000035
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()));
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000100 ASSERT(state_ != DESTROYED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000101 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()));
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000114 ASSERT(state_ != DESTROYED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000115 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 void PostGarbageCollectionProcessing() {
148 if (state_ != Node::PENDING) return;
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) {
ager@chromium.org71daaf62009-04-01 07:22:49 +0000157 v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle());
158 {
159 // Leaving V8.
160 VMState state(EXTERNAL);
161 func(object, par);
162 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000163 }
164 }
165
166 // Place the handle address first to avoid offset computation.
167 Object* object_; // Storage for object pointer.
168
169 // Transition diagram:
170 // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
171 enum State {
172 NORMAL, // Normal global handle.
173 WEAK, // Flagged as weak but not yet finalized.
174 PENDING, // Has been recognized as only reachable by weak handles.
175 NEAR_DEATH, // Callback has informed the handle is near death.
176 DESTROYED
177 };
178 State state_;
179
180 private:
181 // Handle specific callback.
182 WeakReferenceCallback callback_;
183 // Provided data for callback. In DESTROYED state, this is used for
184 // the free list link.
185 union {
186 void* parameter;
187 Node* next_free;
188 } parameter_or_next_free_;
189
190 // Linkage for the list.
191 Node* next_;
192
193 public:
194 TRACK_MEMORY("GlobalHandles::Node")
195};
196
197
198Handle<Object> GlobalHandles::Create(Object* value) {
199 Counters::global_handles.Increment();
200 Node* result;
201 if (first_free() == NULL) {
202 // Allocate a new node.
203 result = new Node(value);
204 result->set_next(head());
205 set_head(result);
206 } else {
207 // Take the first node in the free list.
208 result = first_free();
209 set_first_free(result->next_free());
210 result->Initialize(value);
211 }
212 return result->handle();
213}
214
215
216void GlobalHandles::Destroy(Object** location) {
217 Counters::global_handles.Decrement();
218 if (location == NULL) return;
219 Node* node = Node::FromLocation(location);
220 node->Destroy();
221 // Link the destroyed.
222 node->set_next_free(first_free());
223 set_first_free(node);
224}
225
226
227void GlobalHandles::MakeWeak(Object** location, void* parameter,
228 WeakReferenceCallback callback) {
229 ASSERT(callback != NULL);
230 Node::FromLocation(location)->MakeWeak(parameter, callback);
231}
232
233
234void GlobalHandles::ClearWeakness(Object** location) {
235 Node::FromLocation(location)->ClearWeakness();
236}
237
238
239bool GlobalHandles::IsNearDeath(Object** location) {
240 return Node::FromLocation(location)->IsNearDeath();
241}
242
243
244bool GlobalHandles::IsWeak(Object** location) {
245 return Node::FromLocation(location)->IsWeak();
246}
247
248
249void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
250 // Traversal of GC roots in the global handle list that are marked as
251 // WEAK or PENDING.
252 for (Node* current = head_; current != NULL; current = current->next()) {
253 if (current->state_ == Node::WEAK
254 || current->state_ == Node::PENDING
255 || current->state_ == Node::NEAR_DEATH) {
256 v->VisitPointer(&current->object_);
257 }
258 }
259}
260
261
ager@chromium.org9085a012009-05-11 19:22:57 +0000262void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000263 for (Node* current = head_; current != NULL; current = current->next()) {
264 if (current->state_ == Node::WEAK) {
265 if (f(&current->object_)) {
266 current->state_ = Node::PENDING;
267 LOG(HandleEvent("GlobalHandle::Pending", current->handle().location()));
268 }
269 }
270 }
271}
272
273
274void GlobalHandles::PostGarbageCollectionProcessing() {
275 // Process weak global handle callbacks. This must be done after the
276 // GC is completely done, because the callbacks may invoke arbitrary
277 // API functions.
v8.team.kasperl727e9952008-09-02 14:56:44 +0000278 // At the same time deallocate all DESTROYED nodes
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000279 ASSERT(Heap::gc_state() == Heap::NOT_IN_GC);
280 Node** p = &head_;
281 while (*p != NULL) {
282 (*p)->PostGarbageCollectionProcessing();
283 if ((*p)->state_ == Node::DESTROYED) {
284 // Delete the link.
285 Node* node = *p;
286 *p = node->next(); // Update the link.
287 delete node;
288 } else {
289 p = (*p)->next_addr();
290 }
291 }
292 set_first_free(NULL);
293}
294
295
296void GlobalHandles::IterateRoots(ObjectVisitor* v) {
297 // Traversal of global handles marked as NORMAL or NEAR_DEATH.
298 for (Node* current = head_; current != NULL; current = current->next()) {
299 if (current->state_ == Node::NORMAL) {
300 v->VisitPointer(&current->object_);
301 }
302 }
303}
304
305void GlobalHandles::TearDown() {
306 // Delete all the nodes in the linked list.
307 Node* current = head_;
308 while (current != NULL) {
309 Node* n = current;
310 current = current->next();
311 delete n;
312 }
313 // Reset the head and free_list.
314 set_head(NULL);
315 set_first_free(NULL);
316}
317
318
319int GlobalHandles::number_of_weak_handles_ = 0;
320int GlobalHandles::number_of_global_object_weak_handles_ = 0;
321
322GlobalHandles::Node* GlobalHandles::head_ = NULL;
323GlobalHandles::Node* GlobalHandles::first_free_ = NULL;
324
325#ifdef DEBUG
326
327void GlobalHandles::PrintStats() {
328 int total = 0;
329 int weak = 0;
330 int pending = 0;
331 int near_death = 0;
332 int destroyed = 0;
333
334 for (Node* current = head_; current != NULL; current = current->next()) {
335 total++;
336 if (current->state_ == Node::WEAK) weak++;
337 if (current->state_ == Node::PENDING) pending++;
338 if (current->state_ == Node::NEAR_DEATH) near_death++;
339 if (current->state_ == Node::DESTROYED) destroyed++;
340 }
341
342 PrintF("Global Handle Statistics:\n");
343 PrintF(" allocated memory = %dB\n", sizeof(Node) * total);
344 PrintF(" # weak = %d\n", weak);
345 PrintF(" # pending = %d\n", pending);
346 PrintF(" # near_death = %d\n", near_death);
347 PrintF(" # destroyed = %d\n", destroyed);
348 PrintF(" # total = %d\n", total);
349}
350
351void GlobalHandles::Print() {
352 PrintF("Global handles:\n");
353 for (Node* current = head_; current != NULL; current = current->next()) {
354 PrintF(" handle %p to %p (weak=%d)\n", current->handle().location(),
355 *current->handle(), current->state_ == Node::WEAK);
356 }
357}
358
359#endif
360
ager@chromium.org8bb60582008-12-11 12:02:20 +0000361List<ObjectGroup*>* GlobalHandles::ObjectGroups() {
362 // Lazily initialize the list to avoid startup time static constructors.
363 static List<ObjectGroup*> groups(4);
364 return &groups;
365}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000366
ager@chromium.org8bb60582008-12-11 12:02:20 +0000367void GlobalHandles::AddGroup(Object*** handles, size_t length) {
368 ObjectGroup* new_entry = new ObjectGroup(length);
369 for (size_t i = 0; i < length; ++i)
370 new_entry->objects_.Add(handles[i]);
371 ObjectGroups()->Add(new_entry);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000372}
373
374
375void GlobalHandles::RemoveObjectGroups() {
ager@chromium.org8bb60582008-12-11 12:02:20 +0000376 List<ObjectGroup*>* object_groups = ObjectGroups();
377 for (int i = 0; i< object_groups->length(); i++) {
378 delete object_groups->at(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000379 }
ager@chromium.org8bb60582008-12-11 12:02:20 +0000380 object_groups->Clear();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000381}
382
383
384} } // namespace v8::internal