Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(129)

Side by Side Diff: webkit/port/bindings/v8/V8DOMMap.cpp

Issue 42634: Revert change 12507 and 12532 because it breaks the ... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 11 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « webkit/port/bindings/v8/V8DOMMap.h ('k') | webkit/port/bindings/v8/v8_proxy.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32
33 #include <v8.h>
34
35 #include "DOMObjectsInclude.h"
36 #include "V8DOMMap.h"
37
38 #include <wtf/HashMap.h>
39 #include <wtf/MainThread.h>
40 #include <wtf/Threading.h>
41 #include <wtf/ThreadSpecific.h>
42
43 namespace WebCore {
44
45 // DOM binding algorithm:
46 //
47 // There are two kinds of DOM objects:
48 // 1. DOM tree nodes, such as Document, HTMLElement, ...
49 // there classes implements TreeShared<T> interface;
50 // 2. Non-node DOM objects, such as CSSRule, Location, etc.
51 // these classes implement a ref-counted scheme.
52 //
53 // A DOM object may have a JS wrapper object. If a tree node
54 // is alive, its JS wrapper must be kept alive even it is not
55 // reachable from JS roots.
56 // However, JS wrappers of non-node objects can go away if
57 // not reachable from other JS objects. It works like a cache.
58 //
59 // DOM objects are ref-counted, and JS objects are traced from
60 // a set of root objects. They can create a cycle. To break
61 // cycles, we do following:
62 // Handles from DOM objects to JS wrappers are always weak,
63 // so JS wrappers of non-node object cannot create a cycle.
64 // Before starting a global GC, we create a virtual connection
65 // between nodes in the same tree in the JS heap. If the wrapper
66 // of one node in a tree is alive, wrappers of all nodes in
67 // the same tree are considered alive. This is done by creating
68 // object groups in GC prologue callbacks. The mark-compact
69 // collector will remove these groups after each GC.
70 //
71 // DOM objects should be deref-ed from the owning thread, not the GC thread
72 // that does not own them. In V8, GC can kick in from any thread. To ensure
73 // that DOM objects are always deref-ed from the owning thread when running
74 // V8 in multi-threading environment, we do following:
75 // 1. Maintain a thread specific DOM wrapper map for each object map.
76 // (We're using TLS support from WTF instead of base since V8Bindings
77 // does not depend on base. We further assume that all child threads
78 // running V8 instances are created by WTF and thus a destructor will
79 // be called to clean up all thread specific data.)
80 // 2. When GC happens:
81 // 2.1. If the dead object is in GC thread's map, remove the JS reference
82 // and deref the DOM object.
83 // 2.2. Otherwise, go through all thread maps to find the owning thread.
84 // Remove the JS reference from the owning thread's map and move the
85 // DOM object to a delayed queue. Post a task to the owning thread
86 // to have it deref-ed from the owning thread at later time.
87 // 3. When a thread is tearing down, invoke a cleanup routine to go through
88 // all objects in the delayed queue and the thread map and deref all of
89 // them.
90
91 static void weakDOMObjectCallback(v8::Persistent<v8::Value> obj, void* param);
92 static void weakNodeCallback(v8::Persistent<v8::Value> obj, void* param);
93
94 #if ENABLE(SVG)
95 static void weakSVGElementInstanceCallback(v8::Persistent<v8::Value> obj, void* param);
96
97 // SVG non-node elements may have a reference to a context node which should be notified when the element is change
98 static void weakSVGObjectWithContextCallback(v8::Persistent<v8::Value> obj, void * domObj);
99 #endif
100
101 // This is to ensure that we will deref DOM objects from the owning thread, not the GC thread.
102 // The helper function will be scheduled by the GC thread to get called from the owning thread.
103 static void derefDelayedObjectsInCurrentThread(void*);
104
105 // This should be called to remove all DOM objects associated with the current t hread when it is tearing down.
106 static void removeAllDOMObjectsInCurrentThread();
107
108 // A map from a thread ID to thread's specific data.
109 class ThreadSpecificDOMData;
110 typedef WTF::HashMap<WTF::ThreadIdentifier, ThreadSpecificDOMData*> DOMThreadMap ;
111 static DOMThreadMap domThreadMap;
112
113 // Mutex to protect against concurrent access of domThreadMap.
114 static WTF::Mutex domThreadMapMutex;
115
116 class ThreadSpecificDOMData {
117 public:
118 enum DOMWrapperMapType
119 {
120 DOMNodeMap,
121 DOMObjectMap,
122 ActiveDOMObjectMap,
123 #if ENABLE(SVG)
124 DOMSVGElementInstanceMap,
125 DOMSVGObjectWithContextMap
126 #endif
127 };
128
129 typedef WTF::HashMap<void*, V8ClassIndex::V8WrapperType> DelayedObjectMap;
130
131 template <class KeyType>
132 class InternalDOMWrapperMap : public DOMWrapperMap<KeyType> {
133 public:
134 InternalDOMWrapperMap(v8::WeakReferenceCallback callback)
135 : DOMWrapperMap<KeyType>(callback) { }
136
137 virtual void forget(KeyType* obj);
138
139 void forgetOnly(KeyType* obj)
140 {
141 DOMWrapperMap<KeyType>::forget(obj);
142 }
143 };
144
145 ThreadSpecificDOMData()
146 : m_domNodeMap(new InternalDOMWrapperMap<Node>(&weakNodeCallback))
147 , m_domObjectMap(new InternalDOMWrapperMap<void>(weakDOMObjectCallback))
148 , m_activeDomObjectMap(new InternalDOMWrapperMap<void>(weakActiveDOMObje ctCallback))
149 #if ENABLE(SVG)
150 , m_domSvgElementInstanceMap(new InternalDOMWrapperMap<SVGElementInstanc e>(weakSVGElementInstanceCallback))
151 , m_domSvgObjectWithContextMap(new InternalDOMWrapperMap<void>(weakSVGOb jectWithContextCallback))
152 #endif
153 , m_delayedProcessingScheduled(false)
154 , m_isMainThread(WTF::isMainThread())
155 {
156 WTF::MutexLocker locker(domThreadMapMutex);
157 domThreadMap.set(WTF::currentThread(), this);
158 }
159
160 // This is called when WTF thread is tearing down.
161 // We assume that all child threads running V8 instances are created by WTF.
162 ~ThreadSpecificDOMData()
163 {
164 removeAllDOMObjectsInCurrentThread();
165
166 delete m_domNodeMap;
167 delete m_domObjectMap;
168 delete m_activeDomObjectMap;
169 #if ENABLE(SVG)
170 delete m_domSvgElementInstanceMap;
171 delete m_domSvgObjectWithContextMap;
172 #endif
173
174 WTF::MutexLocker locker(domThreadMapMutex);
175 domThreadMap.remove(WTF::currentThread());
176 }
177
178 void* getDOMWrapperMap(DOMWrapperMapType type)
179 {
180 switch (type) {
181 case DOMNodeMap:
182 return m_domNodeMap;
183 case DOMObjectMap:
184 return m_domObjectMap;
185 case ActiveDOMObjectMap:
186 return m_activeDomObjectMap;
187 #if ENABLE(SVG)
188 case DOMSVGElementInstanceMap:
189 return m_domSvgElementInstanceMap;
190 case DOMSVGObjectWithContextMap:
191 return m_domSvgObjectWithContextMap;
192 #endif
193 default:
194 ASSERT(false);
195 return NULL;
196 }
197 }
198
199 InternalDOMWrapperMap<Node>& domNodeMap() { return *m_domNodeMap; }
200 InternalDOMWrapperMap<void>& domObjectMap() { return *m_domObjectMap; }
201 InternalDOMWrapperMap<void>& activeDomObjectMap() { return *m_activeDomObjec tMap; }
202 #if ENABLE(SVG)
203 InternalDOMWrapperMap<SVGElementInstance>& domSvgElementInstanceMap() { retu rn *m_domSvgElementInstanceMap; }
204 InternalDOMWrapperMap<void>& domSvgObjectWithContextMap() { return *m_domSvg ObjectWithContextMap; }
205 #endif
206
207 DelayedObjectMap& delayedObjectMap() { return m_delayedObjectMap; }
208 bool delayedProcessingScheduled() const { return m_delayedProcessingSchedule d; }
209 void setDelayedProcessingScheduled(bool value) { m_delayedProcessingSchedule d = value; }
210 bool isMainThread() const { return m_isMainThread; }
211
212 private:
213 InternalDOMWrapperMap<Node>* m_domNodeMap;
214 InternalDOMWrapperMap<void>* m_domObjectMap;
215 InternalDOMWrapperMap<void>* m_activeDomObjectMap;
216 #if ENABLE(SVG)
217 InternalDOMWrapperMap<SVGElementInstance>* m_domSvgElementInstanceMap;
218 InternalDOMWrapperMap<void>* m_domSvgObjectWithContextMap;
219 #endif
220
221 // Stores all the DOM objects that are delayed to be processed when the owni ng thread gains control.
222 DelayedObjectMap m_delayedObjectMap;
223
224 // The flag to indicate if the task to do the delayed process has already be en posted.
225 bool m_delayedProcessingScheduled;
226
227 bool m_isMainThread;
228 };
229
230 static WTF::ThreadSpecific<ThreadSpecificDOMData> threadSpecificDOMData;
231
232 template<typename T>
233 static void handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMWrapperMapT ype mapType, V8ClassIndex::V8WrapperType objType, T* obj);
234
235 template <class KeyType>
236 void ThreadSpecificDOMData::InternalDOMWrapperMap<KeyType>::forget(KeyType* obj)
237 {
238 DOMWrapperMap<KeyType>::forget(obj);
239
240 ThreadSpecificDOMData::DelayedObjectMap& delayedObjectMap = (*threadSpecific DOMData).delayedObjectMap();
241 delayedObjectMap.take(obj);
242 }
243
244 DOMWrapperMap<Node>& getDOMNodeMap()
245 {
246 return (*threadSpecificDOMData).domNodeMap();
247 }
248
249 DOMWrapperMap<void>& getDOMObjectMap()
250 {
251 return (*threadSpecificDOMData).domObjectMap();
252 }
253
254 DOMWrapperMap<void>& getActiveDOMObjectMap()
255 {
256 return (*threadSpecificDOMData).activeDomObjectMap();
257 }
258
259 #if ENABLE(SVG)
260 DOMWrapperMap<SVGElementInstance>& getDOMSVGElementInstanceMap()
261 {
262 return (*threadSpecificDOMData).domSvgElementInstanceMap();
263 }
264
265 static void weakSVGElementInstanceCallback(v8::Persistent<v8::Value> obj, void* param)
266 {
267 SVGElementInstance* instance = static_cast<SVGElementInstance*>(param);
268
269 ThreadSpecificDOMData::InternalDOMWrapperMap<SVGElementInstance>& map = stat ic_cast<ThreadSpecificDOMData::InternalDOMWrapperMap<SVGElementInstance>&>(getDO MSVGElementInstanceMap());
270 if (map.contains(instance)) {
271 instance->deref();
272 map.forgetOnly(instance);
273 } else {
274 handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMSVGElementInsta nceMap, V8ClassIndex::SVGELEMENTINSTANCE, instance);
275 }
276 }
277
278 // Map of SVG objects with contexts to V8 objects
279 DOMWrapperMap<void>& getDOMSVGObjectWithContextMap()
280 {
281 return (*threadSpecificDOMData).domSvgObjectWithContextMap();
282 }
283
284 static void weakSVGObjectWithContextCallback(v8::Persistent<v8::Value> obj, void * domObj)
285 {
286 v8::HandleScope scope;
287 ASSERT(obj->IsObject());
288
289 V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(v8::Handle<v8: :Object>::Cast(obj));
290
291 ThreadSpecificDOMData::InternalDOMWrapperMap<void>& map = static_cast<Thread SpecificDOMData::InternalDOMWrapperMap<void>&>(getDOMSVGObjectWithContextMap());
292 if (map.contains(domObj)) {
293 // Forget function removes object from the map and dispose the wrapper.
294 map.forgetOnly(domObj);
295
296 switch (type) {
297 #define MakeCase(TYPE, NAME) \
298 case V8ClassIndex::TYPE: static_cast<NAME*>(domObj)->deref(); break;
299 SVG_OBJECT_TYPES(MakeCase)
300 #undef MakeCase
301 #define MakeCase(TYPE, NAME) \
302 case V8ClassIndex::TYPE: \
303 static_cast<V8SVGPODTypeWrapper<NAME>*>(domObj)->deref(); break;
304 SVG_POD_NATIVE_TYPES(MakeCase)
305 #undef MakeCase
306 default:
307 ASSERT(false);
308 }
309 } else {
310 handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMSVGObjectWithCo ntextMap, type, domObj);
311 }
312 }
313 #endif
314
315 // Called when the dead object is not in GC thread's map. Go through all thread maps to find the one containing it.
316 // Then clear the JS reference and push the DOM object into the delayed queue fo r it to be deref-ed at later time from the owning thread.
317 // * This is called when the GC thread is not the owning thread.
318 // * This can be called on any thread that has GC running.
319 // * Only one V8 instance is running at a time due to V8::Locker. So we don't ne ed to worry about concurrency.
320 template<typename T>
321 static void handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMWrapperMapT ype mapType, V8ClassIndex::V8WrapperType objType, T* obj)
322 {
323 WTF::MutexLocker locker(domThreadMapMutex);
324 for (typename DOMThreadMap::iterator iter(domThreadMap.begin()); iter != dom ThreadMap.end(); ++iter) {
325 WTF::ThreadIdentifier threadID = iter->first;
326 ThreadSpecificDOMData* threadData = iter->second;
327
328 // Skip the current thread that is GC thread.
329 if (threadID == WTF::currentThread()) {
330 ASSERT(!static_cast<DOMWrapperMap<T>*>(threadData->getDOMWrapperMap( mapType))->contains(obj));
331 continue;
332 }
333
334 ThreadSpecificDOMData::InternalDOMWrapperMap<T>* domMap = static_cast<Th readSpecificDOMData::InternalDOMWrapperMap<T>*>(threadData->getDOMWrapperMap(map Type));
335 if (domMap->contains(obj)) {
336 // Clear the JS reference.
337 domMap->forgetOnly(obj);
338
339 // Push into the delayed queue.
340 threadData->delayedObjectMap().set(obj, objType);
341
342 // Post a task to the owning thread in order to process the delayed queue.
343 // FIXME(jianli): For now, we can only post to main thread due to WT F task posting limitation. We will fix this when we work on nested worker.
344 if (!threadData->delayedProcessingScheduled()) {
345 threadData->setDelayedProcessingScheduled(true);
346 if (threadData->isMainThread())
347 WTF::callOnMainThread(&derefDelayedObjectsInCurrentThread, N ULL);
348 }
349
350 break;
351 }
352 }
353 }
354
355 // Called when obj is near death (not reachable from JS roots).
356 // It is time to remove the entry from the table and dispose the handle.
357 static void weakDOMObjectCallback(v8::Persistent<v8::Value> obj, void* domObj)
358 {
359 v8::HandleScope scope;
360 ASSERT(obj->IsObject());
361
362 V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(v8::Handle<v8: :Object>::Cast(obj));
363
364 ThreadSpecificDOMData::InternalDOMWrapperMap<void>& map = static_cast<Thread SpecificDOMData::InternalDOMWrapperMap<void>&>(getDOMObjectMap());
365 if (map.contains(domObj)) {
366 // Forget function removes object from the map and dispose the wrapper.
367 map.forgetOnly(domObj);
368
369 switch (type) {
370 #define MakeCase(TYPE, NAME) \
371 case V8ClassIndex::TYPE: static_cast<NAME*>(domObj)->deref(); break;
372 DOM_OBJECT_TYPES(MakeCase)
373 #undef MakeCase
374 default:
375 ASSERT(false);
376 }
377 } else {
378 handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMObjectMap, type , domObj);
379 }
380 }
381
382 void weakActiveDOMObjectCallback(v8::Persistent<v8::Value> obj, void* domObj)
383 {
384 v8::HandleScope scope;
385 ASSERT(obj->IsObject());
386
387 V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(v8::Handle<v8: :Object>::Cast(obj));
388
389 ThreadSpecificDOMData::InternalDOMWrapperMap<void>& map = static_cast<Thread SpecificDOMData::InternalDOMWrapperMap<void>&>(getActiveDOMObjectMap());
390 if (map.contains(domObj)) {
391 // Forget function removes object from the map and dispose the wrapper.
392 map.forgetOnly(domObj);
393
394 switch (type) {
395 #define MakeCase(TYPE, NAME) \
396 case V8ClassIndex::TYPE: static_cast<NAME*>(domObj)->deref(); break;
397 ACTIVE_DOM_OBJECT_TYPES(MakeCase)
398 #undef MakeCase
399 default:
400 ASSERT(false);
401 }
402 } else {
403 handleWeakObjectInOwningThread(ThreadSpecificDOMData::ActiveDOMObjectMap , type, domObj);
404 }
405 }
406
407 static void weakNodeCallback(v8::Persistent<v8::Value> obj, void* param)
408 {
409 Node* node = static_cast<Node*>(param);
410
411 ThreadSpecificDOMData::InternalDOMWrapperMap<Node>& map = static_cast<Thread SpecificDOMData::InternalDOMWrapperMap<Node>&>(getDOMNodeMap());
412 if (map.contains(node)) {
413 map.forgetOnly(node);
414 node->deref();
415 } else {
416 handleWeakObjectInOwningThread<Node>(ThreadSpecificDOMData::DOMNodeMap, V8ClassIndex::NODE, node);
417 }
418 }
419
420 static void derefObject(V8ClassIndex::V8WrapperType type, void* domObj)
421 {
422 switch (type) {
423 case V8ClassIndex::NODE:
424 static_cast<Node*>(domObj)->deref();
425 break;
426
427 #define MakeCase(TYPE, NAME) \
428 case V8ClassIndex::TYPE: static_cast<NAME*>(domObj)->deref(); break;
429 DOM_OBJECT_TYPES(MakeCase) // This includes both active and non-active .
430 #undef MakeCase
431
432 #if ENABLE(SVG)
433 #define MakeCase(TYPE, NAME) \
434 case V8ClassIndex::TYPE: static_cast<NAME*>(domObj)->deref(); break;
435 SVG_OBJECT_TYPES(MakeCase) // This also includes SVGElementInstance.
436 #undef MakeCase
437
438 #define MakeCase(TYPE, NAME) \
439 case V8ClassIndex::TYPE: \
440 static_cast<V8SVGPODTypeWrapper<NAME>*>(domObj)->deref(); break;
441 SVG_POD_NATIVE_TYPES(MakeCase)
442 #undef MakeCase
443 #endif
444
445 default:
446 ASSERT(false);
447 }
448 }
449
450 static void derefDelayedObjects()
451 {
452 WTF::MutexLocker locker(domThreadMapMutex);
453 ThreadSpecificDOMData::DelayedObjectMap& delayedObjectMap = (*threadSpecific DOMData).delayedObjectMap();
454 for (ThreadSpecificDOMData::DelayedObjectMap::iterator iter(delayedObjectMap .begin()); iter != delayedObjectMap.end(); ++iter) {
455 derefObject(iter->second, iter->first);
456 }
457 delayedObjectMap.clear();
458 }
459
460 static void derefDelayedObjectsInCurrentThread(void*)
461 {
462 (*threadSpecificDOMData).setDelayedProcessingScheduled(false);
463 derefDelayedObjects();
464 }
465
466 template<typename T>
467 static void removeObjectsFromWrapperMap(DOMWrapperMap<T>& domMap)
468 {
469 for (typename WTF::HashMap<T*, v8::Object*>::iterator iter(domMap.impl().beg in()); iter != domMap.impl().end(); ++iter) {
470 T* domObj = static_cast<T*>(iter->first);
471 v8::Persistent<v8::Object> obj(iter->second);
472
473 V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(v8::Handle <v8::Object>::Cast(obj));
474
475 // Deref the DOM object.
476 derefObject(type, domObj);
477
478 // Clear the JS wrapper.
479 obj.Dispose();
480 }
481 domMap.impl().clear();
482 }
483
484 static void removeAllDOMObjectsInCurrentThread()
485 {
486 v8::Locker locker;
487 v8::HandleScope scope;
488
489 // Deref all objects in the delayed queue.
490 derefDelayedObjects();
491
492 // Remove all DOM nodes.
493 removeObjectsFromWrapperMap<Node>(getDOMNodeMap());
494
495 // Remove all DOM objects in the wrapper map.
496 removeObjectsFromWrapperMap<void>(getDOMObjectMap());
497
498 // Remove all active DOM objects in the wrapper map.
499 removeObjectsFromWrapperMap<void>(getActiveDOMObjectMap());
500
501 #if ENABLE(SVG)
502 // Remove all SVG element instances in the wrapper map.
503 removeObjectsFromWrapperMap<SVGElementInstance>(getDOMSVGElementInstanceMap( ));
504
505 // Remove all SVG objects with context in the wrapper map.
506 removeObjectsFromWrapperMap<void>(getDOMSVGObjectWithContextMap());
507 #endif
508 }
509
510 } // namespace WebCore
OLDNEW
« no previous file with comments | « webkit/port/bindings/v8/V8DOMMap.h ('k') | webkit/port/bindings/v8/v8_proxy.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698