blob: 29ad86e0514fd6089a46572f00a578c8352454cc [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2007-2008 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
33namespace v8 { namespace internal {
34
35class GlobalHandles::Node : public Malloced {
36 public:
37
38 void Initialize(Object* object) {
39 // Set the initial value of the handle.
40 object_ = object;
41 state_ = NORMAL;
42 parameter_or_next_free_.parameter = NULL;
43 callback_ = NULL;
44 }
45
46 explicit Node(Object* object) {
47 Initialize(object);
48 // Initialize link structure.
49 next_ = NULL;
50 }
51
52 ~Node() {
53 if (state_ != DESTROYED) Destroy();
54#ifdef DEBUG
55 // Zap the values for eager trapping.
56 object_ = NULL;
57 next_ = NULL;
58 parameter_or_next_free_.next_free = NULL;
59#endif
60 }
61
62 void Destroy() {
63 if (state_ == WEAK || IsNearDeath()) {
64 GlobalHandles::number_of_weak_handles_--;
65 if (object_->IsJSGlobalObject()) {
66 GlobalHandles::number_of_global_object_weak_handles_--;
67 }
68 }
69 state_ = DESTROYED;
70 }
71
72 // Accessors for next_.
73 Node* next() { return next_; }
74 void set_next(Node* value) { next_ = value; }
75 Node** next_addr() { return &next_; }
76
77 // Accessors for next free node in the free list.
78 Node* next_free() {
79 ASSERT(state_ == DESTROYED);
80 return parameter_or_next_free_.next_free;
81 }
82 void set_next_free(Node* value) {
83 ASSERT(state_ == DESTROYED);
84 parameter_or_next_free_.next_free = value;
85 }
86
87 // Returns a link from the handle.
88 static Node* FromLocation(Object** location) {
89 ASSERT(OFFSET_OF(Node, object_) == 0);
90 return reinterpret_cast<Node*>(location);
91 }
92
93 // Returns the handle.
94 Handle<Object> handle() { return Handle<Object>(&object_); }
95
96 // Make this handle weak.
97 void MakeWeak(void* parameter, WeakReferenceCallback callback) {
98 LOG(HandleEvent("GlobalHandle::MakeWeak", handle().location()));
ager@chromium.org3bf7b912008-11-17 09:09:45 +000099 ASSERT(state_ != DESTROYED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000100 if (state_ != WEAK && !IsNearDeath()) {
101 GlobalHandles::number_of_weak_handles_++;
102 if (object_->IsJSGlobalObject()) {
103 GlobalHandles::number_of_global_object_weak_handles_++;
104 }
105 }
106 state_ = WEAK;
107 set_parameter(parameter);
108 callback_ = callback;
109 }
110
111 void ClearWeakness() {
112 LOG(HandleEvent("GlobalHandle::ClearWeakness", handle().location()));
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000113 ASSERT(state_ != DESTROYED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000114 if (state_ == WEAK || IsNearDeath()) {
115 GlobalHandles::number_of_weak_handles_--;
116 if (object_->IsJSGlobalObject()) {
117 GlobalHandles::number_of_global_object_weak_handles_--;
118 }
119 }
120 state_ = NORMAL;
121 set_parameter(NULL);
122 }
123
124 bool IsNearDeath() {
125 // Check for PENDING to ensure correct answer when processing callbacks.
126 return state_ == PENDING || state_ == NEAR_DEATH;
127 }
128
129 bool IsWeak() {
130 return state_ == WEAK;
131 }
132
133 // Returns the id for this weak handle.
134 void set_parameter(void* parameter) {
135 ASSERT(state_ != DESTROYED);
136 parameter_or_next_free_.parameter = parameter;
137 }
138 void* parameter() {
139 ASSERT(state_ != DESTROYED);
140 return parameter_or_next_free_.parameter;
141 }
142
143 // Returns the callback for this weak handle.
144 WeakReferenceCallback callback() { return callback_; }
145
146 void PostGarbageCollectionProcessing() {
147 if (state_ != Node::PENDING) return;
148 LOG(HandleEvent("GlobalHandle::Processing", handle().location()));
149 void* par = parameter();
150 state_ = NEAR_DEATH;
151 set_parameter(NULL);
152 // The callback function is resolved as late as possible to preserve old
153 // behavior.
154 WeakReferenceCallback func = callback();
155 if (func != NULL) {
156 func(v8::Persistent<v8::Object>(ToApi<v8::Object>(handle())), par);
157 }
158 }
159
160 // Place the handle address first to avoid offset computation.
161 Object* object_; // Storage for object pointer.
162
163 // Transition diagram:
164 // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
165 enum State {
166 NORMAL, // Normal global handle.
167 WEAK, // Flagged as weak but not yet finalized.
168 PENDING, // Has been recognized as only reachable by weak handles.
169 NEAR_DEATH, // Callback has informed the handle is near death.
170 DESTROYED
171 };
172 State state_;
173
174 private:
175 // Handle specific callback.
176 WeakReferenceCallback callback_;
177 // Provided data for callback. In DESTROYED state, this is used for
178 // the free list link.
179 union {
180 void* parameter;
181 Node* next_free;
182 } parameter_or_next_free_;
183
184 // Linkage for the list.
185 Node* next_;
186
187 public:
188 TRACK_MEMORY("GlobalHandles::Node")
189};
190
191
192Handle<Object> GlobalHandles::Create(Object* value) {
193 Counters::global_handles.Increment();
194 Node* result;
195 if (first_free() == NULL) {
196 // Allocate a new node.
197 result = new Node(value);
198 result->set_next(head());
199 set_head(result);
200 } else {
201 // Take the first node in the free list.
202 result = first_free();
203 set_first_free(result->next_free());
204 result->Initialize(value);
205 }
206 return result->handle();
207}
208
209
210void GlobalHandles::Destroy(Object** location) {
211 Counters::global_handles.Decrement();
212 if (location == NULL) return;
213 Node* node = Node::FromLocation(location);
214 node->Destroy();
215 // Link the destroyed.
216 node->set_next_free(first_free());
217 set_first_free(node);
218}
219
220
221void GlobalHandles::MakeWeak(Object** location, void* parameter,
222 WeakReferenceCallback callback) {
223 ASSERT(callback != NULL);
224 Node::FromLocation(location)->MakeWeak(parameter, callback);
225}
226
227
228void GlobalHandles::ClearWeakness(Object** location) {
229 Node::FromLocation(location)->ClearWeakness();
230}
231
232
233bool GlobalHandles::IsNearDeath(Object** location) {
234 return Node::FromLocation(location)->IsNearDeath();
235}
236
237
238bool GlobalHandles::IsWeak(Object** location) {
239 return Node::FromLocation(location)->IsWeak();
240}
241
242
243void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
244 // Traversal of GC roots in the global handle list that are marked as
245 // WEAK or PENDING.
246 for (Node* current = head_; current != NULL; current = current->next()) {
247 if (current->state_ == Node::WEAK
248 || current->state_ == Node::PENDING
249 || current->state_ == Node::NEAR_DEATH) {
250 v->VisitPointer(&current->object_);
251 }
252 }
253}
254
255
256void GlobalHandles::MarkWeakRoots(WeakSlotCallback f) {
257 for (Node* current = head_; current != NULL; current = current->next()) {
258 if (current->state_ == Node::WEAK) {
259 if (f(&current->object_)) {
260 current->state_ = Node::PENDING;
261 LOG(HandleEvent("GlobalHandle::Pending", current->handle().location()));
262 }
263 }
264 }
265}
266
267
268void GlobalHandles::PostGarbageCollectionProcessing() {
269 // Process weak global handle callbacks. This must be done after the
270 // GC is completely done, because the callbacks may invoke arbitrary
271 // API functions.
v8.team.kasperl727e9952008-09-02 14:56:44 +0000272 // At the same time deallocate all DESTROYED nodes
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 ASSERT(Heap::gc_state() == Heap::NOT_IN_GC);
274 Node** p = &head_;
275 while (*p != NULL) {
276 (*p)->PostGarbageCollectionProcessing();
277 if ((*p)->state_ == Node::DESTROYED) {
278 // Delete the link.
279 Node* node = *p;
280 *p = node->next(); // Update the link.
281 delete node;
282 } else {
283 p = (*p)->next_addr();
284 }
285 }
286 set_first_free(NULL);
287}
288
289
290void GlobalHandles::IterateRoots(ObjectVisitor* v) {
291 // Traversal of global handles marked as NORMAL or NEAR_DEATH.
292 for (Node* current = head_; current != NULL; current = current->next()) {
293 if (current->state_ == Node::NORMAL) {
294 v->VisitPointer(&current->object_);
295 }
296 }
297}
298
299void GlobalHandles::TearDown() {
300 // Delete all the nodes in the linked list.
301 Node* current = head_;
302 while (current != NULL) {
303 Node* n = current;
304 current = current->next();
305 delete n;
306 }
307 // Reset the head and free_list.
308 set_head(NULL);
309 set_first_free(NULL);
310}
311
312
313int GlobalHandles::number_of_weak_handles_ = 0;
314int GlobalHandles::number_of_global_object_weak_handles_ = 0;
315
316GlobalHandles::Node* GlobalHandles::head_ = NULL;
317GlobalHandles::Node* GlobalHandles::first_free_ = NULL;
318
319#ifdef DEBUG
320
321void GlobalHandles::PrintStats() {
322 int total = 0;
323 int weak = 0;
324 int pending = 0;
325 int near_death = 0;
326 int destroyed = 0;
327
328 for (Node* current = head_; current != NULL; current = current->next()) {
329 total++;
330 if (current->state_ == Node::WEAK) weak++;
331 if (current->state_ == Node::PENDING) pending++;
332 if (current->state_ == Node::NEAR_DEATH) near_death++;
333 if (current->state_ == Node::DESTROYED) destroyed++;
334 }
335
336 PrintF("Global Handle Statistics:\n");
337 PrintF(" allocated memory = %dB\n", sizeof(Node) * total);
338 PrintF(" # weak = %d\n", weak);
339 PrintF(" # pending = %d\n", pending);
340 PrintF(" # near_death = %d\n", near_death);
341 PrintF(" # destroyed = %d\n", destroyed);
342 PrintF(" # total = %d\n", total);
343}
344
345void GlobalHandles::Print() {
346 PrintF("Global handles:\n");
347 for (Node* current = head_; current != NULL; current = current->next()) {
348 PrintF(" handle %p to %p (weak=%d)\n", current->handle().location(),
349 *current->handle(), current->state_ == Node::WEAK);
350 }
351}
352
353#endif
354
355List<ObjectGroup*> GlobalHandles::object_groups_(4);
356
357void GlobalHandles::AddToGroup(void* id, Object** handle) {
358 for (int i = 0; i < object_groups_.length(); i++) {
359 ObjectGroup* entry = object_groups_[i];
360 if (entry->id_ == id) {
361 entry->objects_.Add(handle);
362 return;
363 }
364 }
365
366 // not found
367 ObjectGroup* new_entry = new ObjectGroup(id);
368 new_entry->objects_.Add(handle);
369 object_groups_.Add(new_entry);
370}
371
372
373void GlobalHandles::RemoveObjectGroups() {
374 for (int i = 0; i< object_groups_.length(); i++) {
375 delete object_groups_[i];
376 }
377 object_groups_.Clear();
378}
379
380
381} } // namespace v8::internal