[SCM] WebKit Debian packaging branch, debian/experimental, updated. upstream/1.3.3-9427-gc2be6fc

aroben at apple.com aroben at apple.com
Wed Dec 22 12:51:10 UTC 2010


The following commit has been merged in the debian/experimental branch:
commit a954b53d79e0a03c774316f0c941f9500b8df954
Author: aroben at apple.com <aroben at apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Tue Aug 31 17:53:17 2010 +0000

    Use the Windows thread pool instead of a dedicated thread for WorkQueue on Windows
    
    WorkQueue now uses ::RegisterWaitForSingleObject to find out when work
    items need to be executed. This causes Windows to wait on the objects
    on a thread pool wait thread, and then to spawn a thread pool worker
    thread when an object is signaled. This is more efficient than using
    our own dedicated thread to perform the wait and the work because
    multiple WorkQueues (and even other parts of WebKit or other modules)
    can all share the same wait thread and worker thread pool.
    
    Each time WorkQueue::m_performWorkEvent or any handle in
    WorkQueue::m_handles is signaled, a worker thread will be spawned.
    To maintain WorkQueue's serial nature, only one worker thread is
    allowed to perform work items at a time. (The worker thread that is
    actually performing work items is called the queue's "work thread".)
    To accomplish this, worker threads must register as the queue's work
    thread before performing work items.
    WorkQueue::m_isWorkThreadRegistered is used as an atomic guard to make
    sure that only one worker thread is registered at a time.
    
    Fixes <http://webkit.org/b/43150> <rdar://problem/8247280>.
    
    Reviewed by Anders Carlsson.
    
    * Platform/WorkQueue.h:
      - Added the WorkItemWin class, which is used to wrap WorkItems for
        WorkQueue's Windows implementation
      - Changed m_workItemQueue and m_handles to hold
        RefPtr<WorkItemWin>s
      - Replaced "work queue thread"-related members with new members that
        handle our thread pool code
    
    * Platform/win/WorkQueueWin.cpp:
    (WorkQueue::WorkItemWin::WorkItemWin):
    (WorkQueue::WorkItemWin::create):
    Added simple constructor/creator.
    
    (WorkQueue::handleCallback): Added. This function is called whenever a
    handle in WorkQueue::m_handles is signaled. We add the WorkItemWin
    that corresponds to the handle (passed via the context parameter) to
    the work item queue, then try to register as the work thread and
    perform any queued work. If another thread is already registered as
    the work thread, we just exit and let that thread handle the work we
    queued.
    (WorkQueue::registerHandle): Changed to wrap the WorkItem in a
    WorkItemWin, and to use ::RegisterWaitForSingleObject to wait on the
    handle.
    (WorkQueue::eventCallback): Added. This function is called whenever
    m_performWorkEvent is signaled. We try to register as the work thread
    and perfom any queued work. If another thread is already registered as
    the work thread, we just exit and let that thread handle the work.
    (WorkQueue::performWorkOnRegisteredWorkThread): Added. Performs any
    queued work in a loop until either the queue becomes invalid or no
    work is left to perform. Unregisters as the work thread before exiting
    so that other threads can perform work in the future.
    (WorkQueue::platformInitialize): Added initialization of
    m_isWorkThreadRegistered. Replaced code to spawn the old work queue
    thread with a call to ::RegisterWaitForSingleObject so that a worker
    thread from the thread pool will be spawned when m_performWorkEvent is
    signaled.
    (WorkQueue::tryRegisterAsWorkThread): Added. Attempts an atomic
    compare-and-swap to change m_isWorkThreadRegistered from 0 to 1. If
    sucessful, we return true to indicate that this thread is now
    registered as the work thread.
    (WorkQueue::unregisterAsWorkThread): Added. Uses an atomic
    compare-and-swap to change m_isWorkThreadRegistered back from 1 to 0.
    (WorkQueue::scheduleWork): Changed to wrap the WorkItem in a
    WorkItemWin. Also added an optimization to avoid signaling
    m_performWorkEvent when a work thread is already performing work, as
    it will pick up the item we just queued without us having to do
    anything.
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@66506 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/WebKit2/ChangeLog b/WebKit2/ChangeLog
index 92d0ca1..e6c483c 100644
--- a/WebKit2/ChangeLog
+++ b/WebKit2/ChangeLog
@@ -1,3 +1,78 @@
+2010-08-27  Adam Roben  <aroben at apple.com>
+
+        Use the Windows thread pool instead of a dedicated thread for
+        WorkQueue on Windows
+
+        WorkQueue now uses ::RegisterWaitForSingleObject to find out when work
+        items need to be executed. This causes Windows to wait on the objects
+        on a thread pool wait thread, and then to spawn a thread pool worker
+        thread when an object is signaled. This is more efficient than using
+        our own dedicated thread to perform the wait and the work because
+        multiple WorkQueues (and even other parts of WebKit or other modules)
+        can all share the same wait thread and worker thread pool.
+
+        Each time WorkQueue::m_performWorkEvent or any handle in
+        WorkQueue::m_handles is signaled, a worker thread will be spawned.
+        To maintain WorkQueue's serial nature, only one worker thread is
+        allowed to perform work items at a time. (The worker thread that is
+        actually performing work items is called the queue's "work thread".)
+        To accomplish this, worker threads must register as the queue's work
+        thread before performing work items.
+        WorkQueue::m_isWorkThreadRegistered is used as an atomic guard to make
+        sure that only one worker thread is registered at a time.
+        
+        Fixes <http://webkit.org/b/43150> <rdar://problem/8247280>.
+
+        Reviewed by Anders Carlsson.
+
+        * Platform/WorkQueue.h:
+          - Added the WorkItemWin class, which is used to wrap WorkItems for
+            WorkQueue's Windows implementation
+          - Changed m_workItemQueue and m_handles to hold
+            RefPtr<WorkItemWin>s
+          - Replaced "work queue thread"-related members with new members that
+            handle our thread pool code
+
+        * Platform/win/WorkQueueWin.cpp:
+        (WorkQueue::WorkItemWin::WorkItemWin):
+        (WorkQueue::WorkItemWin::create):
+        Added simple constructor/creator.
+
+        (WorkQueue::handleCallback): Added. This function is called whenever a
+        handle in WorkQueue::m_handles is signaled. We add the WorkItemWin
+        that corresponds to the handle (passed via the context parameter) to
+        the work item queue, then try to register as the work thread and
+        perform any queued work. If another thread is already registered as
+        the work thread, we just exit and let that thread handle the work we
+        queued.
+        (WorkQueue::registerHandle): Changed to wrap the WorkItem in a
+        WorkItemWin, and to use ::RegisterWaitForSingleObject to wait on the
+        handle.
+        (WorkQueue::eventCallback): Added. This function is called whenever
+        m_performWorkEvent is signaled. We try to register as the work thread
+        and perfom any queued work. If another thread is already registered as
+        the work thread, we just exit and let that thread handle the work.
+        (WorkQueue::performWorkOnRegisteredWorkThread): Added. Performs any
+        queued work in a loop until either the queue becomes invalid or no
+        work is left to perform. Unregisters as the work thread before exiting
+        so that other threads can perform work in the future.
+        (WorkQueue::platformInitialize): Added initialization of
+        m_isWorkThreadRegistered. Replaced code to spawn the old work queue
+        thread with a call to ::RegisterWaitForSingleObject so that a worker
+        thread from the thread pool will be spawned when m_performWorkEvent is
+        signaled.
+        (WorkQueue::tryRegisterAsWorkThread): Added. Attempts an atomic
+        compare-and-swap to change m_isWorkThreadRegistered from 0 to 1. If
+        sucessful, we return true to indicate that this thread is now
+        registered as the work thread.
+        (WorkQueue::unregisterAsWorkThread): Added. Uses an atomic
+        compare-and-swap to change m_isWorkThreadRegistered back from 1 to 0.
+        (WorkQueue::scheduleWork): Changed to wrap the WorkItem in a
+        WorkItemWin. Also added an optimization to avoid signaling
+        m_performWorkEvent when a work thread is already performing work, as
+        it will pick up the item we just queued without us having to do
+        anything.
+
 2010-08-31  Csaba Osztrogonác  <ossy at webkit.org>
 
         Reviewed by Antonio Gomes.
diff --git a/WebKit2/Platform/WorkQueue.h b/WebKit2/Platform/WorkQueue.h
index 397cd12..499434b 100644
--- a/WebKit2/Platform/WorkQueue.h
+++ b/WebKit2/Platform/WorkQueue.h
@@ -35,6 +35,7 @@
 #include "WorkItem.h"
 #include <wtf/HashMap.h>
 #include <wtf/PassOwnPtr.h>
+#include <wtf/RefCounted.h>
 #include <wtf/Threading.h>
 #include <wtf/Vector.h>
 
@@ -92,19 +93,36 @@ private:
     dispatch_queue_t m_dispatchQueue;
 #endif
 #elif PLATFORM(WIN)
-    static void* workQueueThreadBody(void*);
-    void workQueueThreadBody();
-    void performWork();
+    class WorkItemWin : public RefCounted<WorkItemWin> {
+    public:
+        static PassRefPtr<WorkItemWin> create(PassOwnPtr<WorkItem>, WorkQueue*);
 
-    ThreadIdentifier m_workQueueThread;
+        WorkItem* item() const { return m_item.get(); }
+        WorkQueue* queue() const { return m_queue; }
+
+    private:
+        WorkItemWin(PassOwnPtr<WorkItem>, WorkQueue*);
+
+        OwnPtr<WorkItem> m_item;
+        WorkQueue* m_queue;
+    };
+
+    static void CALLBACK handleCallback(void* context, BOOLEAN timerOrWaitFired);
+    static void CALLBACK eventCallback(void* context, BOOLEAN timerOrWaitFired);
+
+    bool tryRegisterAsWorkThread();
+    void unregisterAsWorkThread();
+    void performWorkOnRegisteredWorkThread();
 
     HANDLE m_performWorkEvent;
 
+    volatile LONG m_isWorkThreadRegistered;
+
     Mutex m_workItemQueueLock;
-    Vector<WorkItem*> m_workItemQueue;
+    Vector<RefPtr<WorkItemWin> > m_workItemQueue;
 
     Mutex m_handlesLock;
-    HashMap<HANDLE, WorkItem*> m_handles;
+    HashMap<HANDLE, RefPtr<WorkItemWin> > m_handles;
 #elif PLATFORM(QT)
     class WorkItemQt;
     HashMap<QObject*, WorkItemQt*> m_signalListeners;
diff --git a/WebKit2/Platform/win/WorkQueueWin.cpp b/WebKit2/Platform/win/WorkQueueWin.cpp
index 92d734a..284d7ba 100644
--- a/WebKit2/Platform/win/WorkQueueWin.cpp
+++ b/WebKit2/Platform/win/WorkQueueWin.cpp
@@ -25,85 +25,133 @@
 
 #include "WorkQueue.h"
 
-#include <process.h>
 #include <wtf/Threading.h>
 
+inline WorkQueue::WorkItemWin::WorkItemWin(PassOwnPtr<WorkItem> item, WorkQueue* queue)
+    : m_item(item)
+    , m_queue(queue)
+{
+}
+
+PassRefPtr<WorkQueue::WorkItemWin> WorkQueue::WorkItemWin::create(PassOwnPtr<WorkItem> item, WorkQueue* queue)
+{
+    return adoptRef(new WorkItemWin(item, queue));
+}
+
+void WorkQueue::handleCallback(void* context, BOOLEAN timerOrWaitFired)
+{
+    ASSERT_ARG(context, context);
+    ASSERT_ARG(timerOrWaitFired, !timerOrWaitFired);
+
+    WorkItemWin* item = static_cast<WorkItemWin*>(context);
+    WorkQueue* queue = item->queue();
+
+    {
+        MutexLocker lock(queue->m_workItemQueueLock);
+        queue->m_workItemQueue.append(item);
+
+        // If no other thread is performing work, we can do it on this thread.
+        if (!queue->tryRegisterAsWorkThread()) {
+            // Some other thread is performing work. Since we hold the queue lock, we can be sure
+            // that the work thread is not exiting due to an empty queue and will process the work
+            // item we just added to it. If we weren't holding the lock we'd have to signal
+            // m_performWorkEvent to make sure the work item got picked up.
+            return;
+        }
+    }
+
+    queue->performWorkOnRegisteredWorkThread();
+}
+
 void WorkQueue::registerHandle(HANDLE handle, PassOwnPtr<WorkItem> item)
 {
+    RefPtr<WorkItemWin> itemWin = WorkItemWin::create(item, this);
+
     // Add the item.
     {
         MutexLocker locker(m_handlesLock);
-        m_handles.set(handle, item.leakPtr());
+        m_handles.set(handle, itemWin);
     }
 
-    // Set the work event.
-    ::SetEvent(m_performWorkEvent);
+    // FIXME: We need to hold onto waitHandle so that we can unregister the wait later.
+    HANDLE waitHandle;
+    if (!::RegisterWaitForSingleObject(&waitHandle, handle, handleCallback, itemWin.get(), INFINITE, WT_EXECUTEDEFAULT)) {
+        DWORD error = ::GetLastError();
+        ASSERT_NOT_REACHED();
+    }
 }
 
-void* WorkQueue::workQueueThreadBody(void *context)
+void WorkQueue::eventCallback(void* context, BOOLEAN timerOrWaitFired)
 {
-    static_cast<WorkQueue*>(context)->workQueueThreadBody();
-    return 0;
+    ASSERT_ARG(context, context);
+    ASSERT_ARG(timerOrWaitFired, !timerOrWaitFired);
+
+    WorkQueue* queue = static_cast<WorkQueue*>(context);
+
+    if (!queue->tryRegisterAsWorkThread())
+        return;
+
+    queue->performWorkOnRegisteredWorkThread();
 }
 
-void WorkQueue::workQueueThreadBody()
+void WorkQueue::performWorkOnRegisteredWorkThread()
 {
-    while (true) {
-        Vector<HANDLE> handles;
-        {
-            // Copy the handles to our handles vector.
-            MutexLocker locker(m_handlesLock);
-            copyKeysToVector(m_handles, handles);
-        }
+    ASSERT(m_isWorkThreadRegistered);
 
-        // Add the "perform work" event handle.
-        handles.append(m_performWorkEvent);
+    bool isValid = true;
 
-        ASSERT(handles.size() <= MAXIMUM_WAIT_OBJECTS);
+    m_workItemQueueLock.lock();
 
-        // Now we wait.
-        DWORD result = ::WaitForMultipleObjects(handles.size(), handles.data(), FALSE, INFINITE);
-        if (result == WAIT_FAILED) {
-            DWORD error = ::GetLastError();
-            ASSERT_NOT_REACHED();
-        }
-
-        // The wait should never time out since we passed INFINITE for the timeout interval.
-        ASSERT(result != WAIT_TIMEOUT);
-        // We don't know how (or need) to handle abandoned mutexes yet.
-        ASSERT(result < WAIT_ABANDONED_0 || result >= WAIT_ABANDONED_0 + handles.size());
-
-        if (result == handles.size() - 1)
-            performWork();
-        else {
-            // FIXME: If we ever decide to support unregistering handles we would need to copy the hash map.
-            WorkItem* workItem;
-            HANDLE handle = handles[result];
-
-            {
-                MutexLocker locker(m_handlesLock);
-                workItem = m_handles.get(handle);
-            }
-
-            // Execute the work item.
-            workItem->execute();
-        }
+    while (isValid && !m_workItemQueue.isEmpty()) {
+        Vector<RefPtr<WorkItemWin> > workItemQueue;
+        m_workItemQueue.swap(workItemQueue);
 
-        // Check if this queue is invalid.
-        {
+        // Allow more work to be scheduled while we're not using the queue directly.
+        m_workItemQueueLock.unlock();
+        for (size_t i = 0; i < workItemQueue.size(); ++i) {
             MutexLocker locker(m_isValidMutex);
-            if (!m_isValid)
+            isValid = m_isValid;
+            if (!isValid)
                 break;
+            workItemQueue[i]->item()->execute();
         }
+        m_workItemQueueLock.lock();
     }
+
+    // One invariant we maintain is that any work scheduled while a work thread is registered will
+    // be handled by that work thread. Unregister as the work thread while the queue lock is still
+    // held so that no work can be scheduled while we're still registered.
+    unregisterAsWorkThread();
+
+    m_workItemQueueLock.unlock();
 }
 
 void WorkQueue::platformInitialize(const char* name)
 {
+    m_isWorkThreadRegistered = 0;
+
     // Create our event.
-    m_performWorkEvent = ::CreateEvent(0, false, false, 0);
+    m_performWorkEvent = ::CreateEventW(0, FALSE, FALSE, 0);
 
-    m_workQueueThread = createThread(&WorkQueue::workQueueThreadBody, this, name);
+    // FIXME: We need to hold onto waitHandle so that we can unregister the wait later.
+    HANDLE waitHandle;
+    if (!::RegisterWaitForSingleObject(&waitHandle, m_performWorkEvent, eventCallback, this, INFINITE, WT_EXECUTEDEFAULT)) {
+        DWORD error = ::GetLastError();
+        ASSERT_NOT_REACHED();
+    }
+}
+
+bool WorkQueue::tryRegisterAsWorkThread()
+{
+    LONG result = ::InterlockedCompareExchange(&m_isWorkThreadRegistered, 1, 0);
+    ASSERT(!result || result == 1);
+    return !result;
+}
+
+void WorkQueue::unregisterAsWorkThread()
+{
+    LONG result = ::InterlockedCompareExchange(&m_isWorkThreadRegistered, 0, 1);
+    ASSERT_UNUSED(result, result == 1);
 }
 
 void WorkQueue::platformInvalidate()
@@ -116,25 +164,15 @@ void WorkQueue::platformInvalidate()
 void WorkQueue::scheduleWork(PassOwnPtr<WorkItem> item)
 {
     MutexLocker locker(m_workItemQueueLock);
-    m_workItemQueue.append(item.leakPtr());
-
-    // Set the work event.
-    ::SetEvent(m_performWorkEvent);
-}
-
-void WorkQueue::performWork()
-{
-    Vector<WorkItem*> workItemQueue;
-    {
-        MutexLocker locker(m_workItemQueueLock);
-        m_workItemQueue.swap(workItemQueue);
-    }
 
-    for (size_t i = 0; i < workItemQueue.size(); ++i) {
-        OwnPtr<WorkItem> item(workItemQueue[i]);
+    m_workItemQueue.append(WorkItemWin::create(item, this));
 
-        MutexLocker locker(m_isValidMutex);
-        if (m_isValid)
-            item->execute();
-    }
+    // Signal our event so that work thread will perform the work we just added. As an optimization,
+    // we avoid signaling the event if a work thread is already registered. This prevents multiple
+    // work threads from being spawned in most cases. (Note that when a work thread has been spawned
+    // but hasn't registered itself yet, m_isWorkThreadRegistered will be false and we'll end up
+    // spawning a second work thread here. But work thread registration process will ensure that
+    // only one thread actually ends up performing work.)
+    if (!m_isWorkThreadRegistered)
+        ::SetEvent(m_performWorkEvent);
 }

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list