Unreviewed, roll out http://trac.webkit.org/changeset/187972.
[WebKit-https.git] / Source / WebCore / platform / graphics / DisplayRefreshMonitor.cpp
index 23c3f72..f23808c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010, 2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -13,7 +13,7 @@
  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  */
 
 #include "config.h"
+#include "DisplayRefreshMonitor.h"
 
 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
 
-#include "DisplayRefreshMonitor.h"
-
-#include <wtf/CurrentTime.h>
+#include "DisplayRefreshMonitorClient.h"
+#include "DisplayRefreshMonitorIOS.h"
+#include "DisplayRefreshMonitorMac.h"
+#include "DisplayRefreshMonitorManager.h"
 
 namespace WebCore {
 
-DisplayRefreshMonitorClient::DisplayRefreshMonitorClient()
-    : m_scheduled(false)
-    , m_displayIDIsSet(false)
-{
-}
-
-DisplayRefreshMonitorClient::~DisplayRefreshMonitorClient()
+RefPtr<DisplayRefreshMonitor> DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(PlatformDisplayID displayID)
 {
-    DisplayRefreshMonitorManager::sharedManager()->unregisterClient(this);
+#if PLATFORM(MAC)
+    return DisplayRefreshMonitorMac::create(displayID);
+#endif
+#if PLATFORM(IOS)
+    return DisplayRefreshMonitorIOS::create(displayID);
+#endif
+    return nullptr;
 }
 
-void DisplayRefreshMonitorClient::fireDisplayRefreshIfNeeded(double timestamp)
+RefPtr<DisplayRefreshMonitor> DisplayRefreshMonitor::create(DisplayRefreshMonitorClient& client)
 {
-    if (m_scheduled) {
-        m_scheduled = false;
-        displayRefreshFired(timestamp);
-    }
+    return client.createDisplayRefreshMonitor(client.displayID());
 }
 
 DisplayRefreshMonitor::DisplayRefreshMonitor(PlatformDisplayID displayID)
-    : m_timestamp(0)
+    : m_monotonicAnimationStartTime(0)
     , m_active(true)
     , m_scheduled(false)
     , m_previousFrameDone(true)
+    , m_unscheduledFireCount(0)
     , m_displayID(displayID)
-#if PLATFORM(MAC)
-    , m_displayLink(0)
-#endif
+    , m_clientsToBeNotified(nullptr)
 {
 }
 
-void DisplayRefreshMonitor::refreshDisplayOnMainThread(void* data)
+DisplayRefreshMonitor::~DisplayRefreshMonitor()
 {
-    DisplayRefreshMonitor* monitor = static_cast<DisplayRefreshMonitor*>(data);
-    monitor->notifyClients();
 }
 
-void DisplayRefreshMonitor::notifyClients()
+void DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread(void* data)
 {
-    double timestamp;
-    {
-        MutexLocker lock(m_mutex);
-        m_scheduled = false;
-        timestamp = m_timestamp;
-    }
-
-    for (size_t i = 0; i < m_clients.size(); ++i)
-        m_clients[i]->fireDisplayRefreshIfNeeded(timestamp);
-
-    {
-        MutexLocker lock(m_mutex);
-        m_previousFrameDone = true;
-    }
+    DisplayRefreshMonitor* monitor = static_cast<DisplayRefreshMonitor*>(data);
+    monitor->displayDidRefresh();
 }
 
-DisplayRefreshMonitorManager* DisplayRefreshMonitorManager::sharedManager()
+void DisplayRefreshMonitor::addClient(DisplayRefreshMonitorClient& client)
 {
-    DEFINE_STATIC_LOCAL(DisplayRefreshMonitorManager, manager, ());
-    return &manager;
+    m_clients.add(&client);
 }
 
-size_t DisplayRefreshMonitorManager::findMonitor(PlatformDisplayID displayID) const
+bool DisplayRefreshMonitor::removeClient(DisplayRefreshMonitorClient& client)
 {
-    for (size_t i = 0; i < m_monitors.size(); ++i)
-        if (m_monitors[i]->displayID() == displayID)
-            return i;
-            
-    return notFound;
+    if (m_clientsToBeNotified)
+        m_clientsToBeNotified->remove(&client);
+    return m_clients.remove(&client);
 }
 
-void DisplayRefreshMonitorManager::registerClient(DisplayRefreshMonitorClient* client)
+void DisplayRefreshMonitor::displayDidRefresh()
 {
-    if (!client->m_displayIDIsSet)
-        return;
-        
-    size_t index = findMonitor(client->m_displayID);
-    DisplayRefreshMonitor* monitor;
-    
-    if (index == notFound) {
-        monitor = new DisplayRefreshMonitor(client->m_displayID);
-        m_monitors.append(monitor);
-    } else
-        monitor = m_monitors[index];
-        
-    monitor->addClient(client);
-}
+    double monotonicAnimationStartTime;
 
-void DisplayRefreshMonitorManager::unregisterClient(DisplayRefreshMonitorClient* client)
-{
-    if (!client->m_displayIDIsSet)
-        return;
-        
-    size_t index = findMonitor(client->m_displayID);
-    if (index == notFound)
-        return;
-    
-    DisplayRefreshMonitor* monitor = m_monitors[index];
-    if (monitor->removeClient(client)) {
-        if (!monitor->hasClients()) {
-            m_monitors.remove(index);
-            delete monitor;
-        }
+    {
+        MutexLocker lock(m_mutex);
+        if (!m_scheduled)
+            ++m_unscheduledFireCount;
+        else
+            m_unscheduledFireCount = 0;
+
+        m_scheduled = false;
+        monotonicAnimationStartTime = m_monotonicAnimationStartTime;
     }
-}
 
-bool DisplayRefreshMonitorManager::scheduleAnimation(DisplayRefreshMonitorClient* client)
-{
-    if (!client->m_displayIDIsSet)
-        return false;
-        
-    size_t i = findMonitor(client->m_displayID);
-    ASSERT(i != notFound);
-
-    client->m_scheduled = true;
-    return m_monitors[i]->requestRefreshCallback();
-}
+    // The call back can cause all our clients to be unregistered, so we need to protect
+    // against deletion until the end of the method.
+    Ref<DisplayRefreshMonitor> protect(*this);
+
+    // Copy the hash table and remove clients from it one by one so we don't notify
+    // any client twice, but can respond to removal of clients during the delivery process.
+    HashSet<DisplayRefreshMonitorClient*> clientsToBeNotified = m_clients;
+    m_clientsToBeNotified = &clientsToBeNotified;
+    while (!clientsToBeNotified.isEmpty()) {
+        DisplayRefreshMonitorClient* client = clientsToBeNotified.takeAny();
+        client->fireDisplayRefreshIfNeeded(monotonicAnimationStartTime);
+
+        // This checks if this function was reentered. In that case, stop iterating
+        // since it's not safe to use the set any more.
+        if (m_clientsToBeNotified != &clientsToBeNotified)
+            break;
+    }
 
-void DisplayRefreshMonitorManager::windowScreenDidChange(PlatformDisplayID displayID, DisplayRefreshMonitorClient* client)
-{
-    if (client->m_displayIDIsSet && client->m_displayID == displayID)
-        return;
+    if (m_clientsToBeNotified == &clientsToBeNotified)
+        m_clientsToBeNotified = nullptr;
+
+    {
+        MutexLocker lock(m_mutex);
+        m_previousFrameDone = true;
+    }
     
-    unregisterClient(client);
-    client->setDisplayID(displayID);
-    registerClient(client);
-    if (client->m_scheduled)
-        scheduleAnimation(client);
+    DisplayRefreshMonitorManager::sharedManager().displayDidRefresh(*this);
 }
 
 }