PageRenderTime 39ms CodeModel.GetById 13ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 0ms

/platform/external/webkit/WebCore/bindings/v8/V8GCController.cpp

https://github.com/aharish/totoro-gb-opensource-update2
C++ | 454 lines | 284 code | 60 blank | 110 comment | 47 complexity | 85e6e2d7549369e5ae8122452a1f044d MD5 | raw file
  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#include "V8GCController.h"
 33
 34#include "DOMDataStore.h"
 35#include "DOMObjectsInclude.h"
 36#include "V8DOMMap.h"
 37#include "V8Index.h"
 38#include "V8Proxy.h"
 39
 40#include <algorithm>
 41#include <utility>
 42#include <v8.h>
 43#include <v8-debug.h>
 44#include <wtf/HashMap.h>
 45#include <wtf/StdLibExtras.h>
 46#include <wtf/UnusedParam.h>
 47
 48namespace WebCore {
 49
 50#ifndef NDEBUG
 51// Keeps track of global handles created (not JS wrappers
 52// of DOM objects). Often these global handles are source
 53// of leaks.
 54//
 55// If you want to let a C++ object hold a persistent handle
 56// to a JS object, you should register the handle here to
 57// keep track of leaks.
 58//
 59// When creating a persistent handle, call:
 60//
 61// #ifndef NDEBUG
 62//    V8GCController::registerGlobalHandle(type, host, handle);
 63// #endif
 64//
 65// When releasing the handle, call:
 66//
 67// #ifndef NDEBUG
 68//    V8GCController::unregisterGlobalHandle(type, host, handle);
 69// #endif
 70//
 71typedef HashMap<v8::Value*, GlobalHandleInfo*> GlobalHandleMap;
 72
 73static GlobalHandleMap& globalHandleMap()
 74{
 75    DEFINE_STATIC_LOCAL(GlobalHandleMap, staticGlobalHandleMap, ());
 76    return staticGlobalHandleMap;
 77}
 78
 79// The function is the place to set the break point to inspect
 80// live global handles. Leaks are often come from leaked global handles.
 81static void enumerateGlobalHandles()
 82{
 83    for (GlobalHandleMap::iterator it = globalHandleMap().begin(), end = globalHandleMap().end(); it != end; ++it) {
 84        GlobalHandleInfo* info = it->second;
 85        UNUSED_PARAM(info);
 86        v8::Value* handle = it->first;
 87        UNUSED_PARAM(handle);
 88    }
 89}
 90
 91void V8GCController::registerGlobalHandle(GlobalHandleType type, void* host, v8::Persistent<v8::Value> handle)
 92{
 93    ASSERT(!globalHandleMap().contains(*handle));
 94    globalHandleMap().set(*handle, new GlobalHandleInfo(host, type));
 95}
 96
 97void V8GCController::unregisterGlobalHandle(void* host, v8::Persistent<v8::Value> handle)
 98{
 99    ASSERT(globalHandleMap().contains(*handle));
100    GlobalHandleInfo* info = globalHandleMap().take(*handle);
101    ASSERT(info->m_host == host);
102    delete info;
103}
104#endif // ifndef NDEBUG
105
106typedef HashMap<Node*, v8::Object*> DOMNodeMap;
107typedef HashMap<void*, v8::Object*> DOMObjectMap;
108
109#ifndef NDEBUG
110
111static void enumerateDOMObjectMap(DOMObjectMap& wrapperMap)
112{
113    for (DOMObjectMap::iterator it = wrapperMap.begin(), end = wrapperMap.end(); it != end; ++it) {
114        v8::Persistent<v8::Object> wrapper(it->second);
115        V8ClassIndex::V8WrapperType type = V8DOMWrapper::domWrapperType(wrapper);
116        void* object = it->first;
117        UNUSED_PARAM(type);
118        UNUSED_PARAM(object);
119    }
120}
121
122class DOMObjectVisitor : public DOMWrapperMap<void>::Visitor {
123public:
124    void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper)
125    {
126        V8ClassIndex::V8WrapperType type = V8DOMWrapper::domWrapperType(wrapper);
127        UNUSED_PARAM(type);
128        UNUSED_PARAM(object);
129    }
130};
131
132class EnsureWeakDOMNodeVisitor : public DOMWrapperMap<Node>::Visitor {
133public:
134    void visitDOMWrapper(Node* object, v8::Persistent<v8::Object> wrapper)
135    {
136        UNUSED_PARAM(object);
137        ASSERT(wrapper.IsWeak());
138    }
139};
140
141#endif // NDEBUG
142
143// A map from a DOM node to its JS wrapper, the wrapper
144// is kept as a strong reference to survive GCs.
145static DOMObjectMap& gcProtectedMap()
146{
147    DEFINE_STATIC_LOCAL(DOMObjectMap, staticGcProtectedMap, ());
148    return staticGcProtectedMap;
149}
150
151void V8GCController::gcProtect(void* domObject)
152{
153    if (!domObject)
154        return;
155    if (gcProtectedMap().contains(domObject))
156        return;
157    if (!getDOMObjectMap().contains(domObject))
158        return;
159
160    // Create a new (strong) persistent handle for the object.
161    v8::Persistent<v8::Object> wrapper = getDOMObjectMap().get(domObject);
162    if (wrapper.IsEmpty())
163        return;
164
165    gcProtectedMap().set(domObject, *v8::Persistent<v8::Object>::New(wrapper));
166}
167
168void V8GCController::gcUnprotect(void* domObject)
169{
170    if (!domObject)
171        return;
172    if (!gcProtectedMap().contains(domObject))
173        return;
174
175    // Dispose the strong reference.
176    v8::Persistent<v8::Object> wrapper(gcProtectedMap().take(domObject));
177    wrapper.Dispose();
178}
179
180class GCPrologueVisitor : public DOMWrapperMap<void>::Visitor {
181public:
182    void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper)
183    {
184        ASSERT(wrapper.IsWeak());
185        V8ClassIndex::V8WrapperType type = V8DOMWrapper::domWrapperType(wrapper);
186        switch (type) {
187#define MAKE_CASE(TYPE, NAME)                             \
188        case V8ClassIndex::TYPE: {                    \
189            NAME* impl = static_cast<NAME*>(object);  \
190            if (impl->hasPendingActivity())           \
191                wrapper.ClearWeak();                  \
192            break;                                    \
193        }
194    ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE)
195    default:
196        ASSERT_NOT_REACHED();
197#undef MAKE_CASE
198        }
199
200    // Additional handling of message port ensuring that entangled ports also
201    // have their wrappers entangled. This should ideally be handled when the
202    // ports are actually entangled in MessagePort::entangle, but to avoid
203    // forking MessagePort.* this is postponed to GC time. Having this postponed
204    // has the drawback that the wrappers are "entangled/unentangled" for each
205    // GC even though their entaglement most likely is still the same.
206    if (type == V8ClassIndex::MESSAGEPORT) {
207        // Mark each port as in-use if it's entangled. For simplicity's sake, we assume all ports are remotely entangled,
208        // since the Chromium port implementation can't tell the difference.
209        MessagePort* port1 = static_cast<MessagePort*>(object);
210        if (port1->isEntangled())
211            wrapper.ClearWeak();
212    }
213}
214};
215
216class GrouperItem {
217public:
218    GrouperItem(uintptr_t groupId, Node* node, v8::Persistent<v8::Object> wrapper) 
219        : m_groupId(groupId)
220        , m_node(node)
221        , m_wrapper(wrapper) 
222        {
223        }
224
225    uintptr_t groupId() const { return m_groupId; }
226    Node* node() const { return m_node; }
227    v8::Persistent<v8::Object> wrapper() const { return m_wrapper; }
228
229private:
230    uintptr_t m_groupId;
231    Node* m_node;
232    v8::Persistent<v8::Object> m_wrapper;
233};
234
235bool operator<(const GrouperItem& a, const GrouperItem& b)
236{
237    return a.groupId() < b.groupId();
238}
239
240typedef Vector<GrouperItem> GrouperList;
241
242class ObjectGrouperVisitor : public DOMWrapperMap<Node>::Visitor {
243public:
244    ObjectGrouperVisitor()
245    {
246        // FIXME: grouper_.reserveCapacity(node_map.size());  ?
247    }
248
249    void visitDOMWrapper(Node* node, v8::Persistent<v8::Object> wrapper)
250    {
251
252        // If the node is in document, put it in the ownerDocument's object group.
253        //
254        // If an image element was created by JavaScript "new Image",
255        // it is not in a document. However, if the load event has not
256        // been fired (still onloading), it is treated as in the document.
257        //
258        // Otherwise, the node is put in an object group identified by the root
259        // element of the tree to which it belongs.
260        uintptr_t groupId;
261        if (node->inDocument() || (node->hasTagName(HTMLNames::imgTag) && !static_cast<HTMLImageElement*>(node)->haveFiredLoadEvent()))
262            groupId = reinterpret_cast<uintptr_t>(node->document());
263        else {
264            Node* root = node;
265            if (node->isAttributeNode()) {
266                root = static_cast<Attr*>(node)->ownerElement();
267                // If the attribute has no element, no need to put it in the group,
268                // because it'll always be a group of 1.
269                if (!root)
270                    return;
271            } else {
272                while (root->parent())
273                    root = root->parent();
274
275                // If the node is alone in its DOM tree (doesn't have a parent or any
276                // children) then the group will be filtered out later anyway.
277                if (root == node && !node->hasChildNodes() && !node->hasAttributes())
278                    return;
279            }
280            groupId = reinterpret_cast<uintptr_t>(root);
281        }
282        m_grouper.append(GrouperItem(groupId, node, wrapper));
283    }
284
285    void applyGrouping()
286    {
287        // Group by sorting by the group id.
288        std::sort(m_grouper.begin(), m_grouper.end());
289
290        // FIXME Should probably work in iterators here, but indexes were easier for my simple mind.
291        for (size_t i = 0; i < m_grouper.size(); ) {
292            // Seek to the next key (or the end of the list).
293            size_t nextKeyIndex = m_grouper.size();
294            for (size_t j = i; j < m_grouper.size(); ++j) {
295                if (m_grouper[i].groupId() != m_grouper[j].groupId()) {
296                    nextKeyIndex = j;
297                    break;
298                }
299            }
300
301            ASSERT(nextKeyIndex > i);
302
303            // We only care about a group if it has more than one object. If it only
304            // has one object, it has nothing else that needs to be kept alive.
305            if (nextKeyIndex - i <= 1) {
306                i = nextKeyIndex;
307                continue;
308            }
309
310            Vector<v8::Persistent<v8::Value> > group;
311            group.reserveCapacity(nextKeyIndex - i);
312            for (; i < nextKeyIndex; ++i) {
313                v8::Persistent<v8::Value> wrapper = m_grouper[i].wrapper();
314                if (!wrapper.IsEmpty())
315                    group.append(wrapper);
316                /* FIXME: Re-enabled this code to avoid GCing these wrappers!
317                             Currently this depends on looking up the wrapper
318                             during a GC, but we don't know which isolated world
319                             we're in, so it's unclear which map to look in...
320
321                // If the node is styled and there is a wrapper for the inline
322                // style declaration, we need to keep that style declaration
323                // wrapper alive as well, so we add it to the object group.
324                if (node->isStyledElement()) {
325                  StyledElement* element = reinterpret_cast<StyledElement*>(node);
326                  CSSStyleDeclaration* style = element->inlineStyleDecl();
327                  if (style != NULL) {
328                    wrapper = getDOMObjectMap().get(style);
329                    if (!wrapper.IsEmpty())
330                      group.append(wrapper);
331                  }
332                }
333                */
334            }
335
336            if (group.size() > 1)
337                v8::V8::AddObjectGroup(&group[0], group.size());
338
339            ASSERT(i == nextKeyIndex);
340        }
341    }
342
343private:
344    GrouperList m_grouper;
345};
346
347// Create object groups for DOM tree nodes.
348void V8GCController::gcPrologue()
349{
350    v8::HandleScope scope;
351
352#ifndef NDEBUG
353    DOMObjectVisitor domObjectVisitor;
354    visitDOMObjectsInCurrentThread(&domObjectVisitor);
355#endif
356
357    // Run through all objects with possible pending activity making their
358    // wrappers non weak if there is pending activity.
359    GCPrologueVisitor prologueVisitor;
360    visitActiveDOMObjectsInCurrentThread(&prologueVisitor);
361
362    // Create object groups.
363    ObjectGrouperVisitor objectGrouperVisitor;
364    visitDOMNodesInCurrentThread(&objectGrouperVisitor);
365    objectGrouperVisitor.applyGrouping();
366}
367
368class GCEpilogueVisitor : public DOMWrapperMap<void>::Visitor {
369public:
370    void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper)
371    {
372        V8ClassIndex::V8WrapperType type = V8DOMWrapper::domWrapperType(wrapper);
373        switch (type) {
374#define MAKE_CASE(TYPE, NAME)                                                   \
375        case V8ClassIndex::TYPE: {                                              \
376          NAME* impl = static_cast<NAME*>(object);                              \
377          if (impl->hasPendingActivity()) {                                     \
378            ASSERT(!wrapper.IsWeak());                                          \
379            wrapper.MakeWeak(impl, &DOMDataStore::weakActiveDOMObjectCallback); \
380          }                                                                     \
381          break;                                                                \
382        }
383ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE)
384        default:
385            ASSERT_NOT_REACHED();
386#undef MAKE_CASE
387        }
388
389        if (type == V8ClassIndex::MESSAGEPORT) {
390            MessagePort* port1 = static_cast<MessagePort*>(object);
391            // We marked this port as reachable in GCPrologueVisitor.  Undo this now since the
392            // port could be not reachable in the future if it gets disentangled (and also
393            // GCPrologueVisitor expects to see all handles marked as weak).
394            if (!wrapper.IsWeak() && !wrapper.IsNearDeath())
395                wrapper.MakeWeak(port1, &DOMDataStore::weakActiveDOMObjectCallback);
396        }
397    }
398};
399
400int V8GCController::workingSetEstimateMB = 0;
401
402namespace {
403
404int getMemoryUsageInMB()
405{
406#if PLATFORM(CHROMIUM)
407    return ChromiumBridge::memoryUsageMB();
408#else
409    return 0;
410#endif
411}
412
413}  // anonymous namespace
414
415void V8GCController::gcEpilogue()
416{
417    v8::HandleScope scope;
418
419    // Run through all objects with pending activity making their wrappers weak
420    // again.
421    GCEpilogueVisitor epilogueVisitor;
422    visitActiveDOMObjectsInCurrentThread(&epilogueVisitor);
423
424    workingSetEstimateMB = getMemoryUsageInMB();
425
426#ifndef NDEBUG
427    // Check all survivals are weak.
428    DOMObjectVisitor domObjectVisitor;
429    visitDOMObjectsInCurrentThread(&domObjectVisitor);
430
431    EnsureWeakDOMNodeVisitor weakDOMNodeVisitor;
432    visitDOMNodesInCurrentThread(&weakDOMNodeVisitor);
433
434    enumerateDOMObjectMap(gcProtectedMap());
435    enumerateGlobalHandles();
436#endif
437}
438
439void V8GCController::checkMemoryUsage()
440{
441#if PLATFORM(CHROMIUM)
442    // These values are appropriate for Chromium only.
443    const int lowUsageMB = 256;  // If memory usage is below this threshold, do not bother forcing GC.
444    const int highUsageMB = 1024;  // If memory usage is above this threshold, force GC more aggresively.
445    const int highUsageDeltaMB = 128;  // Delta of memory usage growth (vs. last workingSetEstimateMB) to force GC when memory usage is high.
446
447    int memoryUsageMB = getMemoryUsageInMB();
448    if ((memoryUsageMB > lowUsageMB && memoryUsageMB > 2 * workingSetEstimateMB) || (memoryUsageMB > highUsageMB && memoryUsageMB > workingSetEstimateMB + highUsageDeltaMB))
449        v8::V8::LowMemoryNotification();
450#endif
451}
452
453
454}  // namespace WebCore