Changeset 66506 in webkit
- Timestamp:
- Aug 31, 2010 10:53:17 AM (14 years ago)
- Location:
- trunk/WebKit2
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebKit2/ChangeLog
r66495 r66506 1 2010-08-27 Adam Roben <aroben@apple.com> 2 3 Use the Windows thread pool instead of a dedicated thread for 4 WorkQueue on Windows 5 6 WorkQueue now uses ::RegisterWaitForSingleObject to find out when work 7 items need to be executed. This causes Windows to wait on the objects 8 on a thread pool wait thread, and then to spawn a thread pool worker 9 thread when an object is signaled. This is more efficient than using 10 our own dedicated thread to perform the wait and the work because 11 multiple WorkQueues (and even other parts of WebKit or other modules) 12 can all share the same wait thread and worker thread pool. 13 14 Each time WorkQueue::m_performWorkEvent or any handle in 15 WorkQueue::m_handles is signaled, a worker thread will be spawned. 16 To maintain WorkQueue's serial nature, only one worker thread is 17 allowed to perform work items at a time. (The worker thread that is 18 actually performing work items is called the queue's "work thread".) 19 To accomplish this, worker threads must register as the queue's work 20 thread before performing work items. 21 WorkQueue::m_isWorkThreadRegistered is used as an atomic guard to make 22 sure that only one worker thread is registered at a time. 23 24 Fixes <http://webkit.org/b/43150> <rdar://problem/8247280>. 25 26 Reviewed by Anders Carlsson. 27 28 * Platform/WorkQueue.h: 29 - Added the WorkItemWin class, which is used to wrap WorkItems for 30 WorkQueue's Windows implementation 31 - Changed m_workItemQueue and m_handles to hold 32 RefPtr<WorkItemWin>s 33 - Replaced "work queue thread"-related members with new members that 34 handle our thread pool code 35 36 * Platform/win/WorkQueueWin.cpp: 37 (WorkQueue::WorkItemWin::WorkItemWin): 38 (WorkQueue::WorkItemWin::create): 39 Added simple constructor/creator. 40 41 (WorkQueue::handleCallback): Added. This function is called whenever a 42 handle in WorkQueue::m_handles is signaled. We add the WorkItemWin 43 that corresponds to the handle (passed via the context parameter) to 44 the work item queue, then try to register as the work thread and 45 perform any queued work. If another thread is already registered as 46 the work thread, we just exit and let that thread handle the work we 47 queued. 48 (WorkQueue::registerHandle): Changed to wrap the WorkItem in a 49 WorkItemWin, and to use ::RegisterWaitForSingleObject to wait on the 50 handle. 51 (WorkQueue::eventCallback): Added. This function is called whenever 52 m_performWorkEvent is signaled. We try to register as the work thread 53 and perfom any queued work. If another thread is already registered as 54 the work thread, we just exit and let that thread handle the work. 55 (WorkQueue::performWorkOnRegisteredWorkThread): Added. Performs any 56 queued work in a loop until either the queue becomes invalid or no 57 work is left to perform. Unregisters as the work thread before exiting 58 so that other threads can perform work in the future. 59 (WorkQueue::platformInitialize): Added initialization of 60 m_isWorkThreadRegistered. Replaced code to spawn the old work queue 61 thread with a call to ::RegisterWaitForSingleObject so that a worker 62 thread from the thread pool will be spawned when m_performWorkEvent is 63 signaled. 64 (WorkQueue::tryRegisterAsWorkThread): Added. Attempts an atomic 65 compare-and-swap to change m_isWorkThreadRegistered from 0 to 1. If 66 sucessful, we return true to indicate that this thread is now 67 registered as the work thread. 68 (WorkQueue::unregisterAsWorkThread): Added. Uses an atomic 69 compare-and-swap to change m_isWorkThreadRegistered back from 1 to 0. 70 (WorkQueue::scheduleWork): Changed to wrap the WorkItem in a 71 WorkItemWin. Also added an optimization to avoid signaling 72 m_performWorkEvent when a work thread is already performing work, as 73 it will pick up the item we just queued without us having to do 74 anything. 75 1 76 2010-08-31 Csaba Osztrogonác <ossy@webkit.org> 2 77 -
trunk/WebKit2/Platform/WorkQueue.h
r63148 r66506 36 36 #include <wtf/HashMap.h> 37 37 #include <wtf/PassOwnPtr.h> 38 #include <wtf/RefCounted.h> 38 39 #include <wtf/Threading.h> 39 40 #include <wtf/Vector.h> … … 93 94 #endif 94 95 #elif PLATFORM(WIN) 95 static void* workQueueThreadBody(void*);96 void workQueueThreadBody();97 void performWork();96 class WorkItemWin : public RefCounted<WorkItemWin> { 97 public: 98 static PassRefPtr<WorkItemWin> create(PassOwnPtr<WorkItem>, WorkQueue*); 98 99 99 ThreadIdentifier m_workQueueThread; 100 WorkItem* item() const { return m_item.get(); } 101 WorkQueue* queue() const { return m_queue; } 102 103 private: 104 WorkItemWin(PassOwnPtr<WorkItem>, WorkQueue*); 105 106 OwnPtr<WorkItem> m_item; 107 WorkQueue* m_queue; 108 }; 109 110 static void CALLBACK handleCallback(void* context, BOOLEAN timerOrWaitFired); 111 static void CALLBACK eventCallback(void* context, BOOLEAN timerOrWaitFired); 112 113 bool tryRegisterAsWorkThread(); 114 void unregisterAsWorkThread(); 115 void performWorkOnRegisteredWorkThread(); 100 116 101 117 HANDLE m_performWorkEvent; 102 118 119 volatile LONG m_isWorkThreadRegistered; 120 103 121 Mutex m_workItemQueueLock; 104 Vector< WorkItem*> m_workItemQueue;122 Vector<RefPtr<WorkItemWin> > m_workItemQueue; 105 123 106 124 Mutex m_handlesLock; 107 HashMap<HANDLE, WorkItem*> m_handles;125 HashMap<HANDLE, RefPtr<WorkItemWin> > m_handles; 108 126 #elif PLATFORM(QT) 109 127 class WorkItemQt; -
trunk/WebKit2/Platform/win/WorkQueueWin.cpp
r63912 r66506 26 26 #include "WorkQueue.h" 27 27 28 #include <process.h>29 28 #include <wtf/Threading.h> 29 30 inline WorkQueue::WorkItemWin::WorkItemWin(PassOwnPtr<WorkItem> item, WorkQueue* queue) 31 : m_item(item) 32 , m_queue(queue) 33 { 34 } 35 36 PassRefPtr<WorkQueue::WorkItemWin> WorkQueue::WorkItemWin::create(PassOwnPtr<WorkItem> item, WorkQueue* queue) 37 { 38 return adoptRef(new WorkItemWin(item, queue)); 39 } 40 41 void WorkQueue::handleCallback(void* context, BOOLEAN timerOrWaitFired) 42 { 43 ASSERT_ARG(context, context); 44 ASSERT_ARG(timerOrWaitFired, !timerOrWaitFired); 45 46 WorkItemWin* item = static_cast<WorkItemWin*>(context); 47 WorkQueue* queue = item->queue(); 48 49 { 50 MutexLocker lock(queue->m_workItemQueueLock); 51 queue->m_workItemQueue.append(item); 52 53 // If no other thread is performing work, we can do it on this thread. 54 if (!queue->tryRegisterAsWorkThread()) { 55 // Some other thread is performing work. Since we hold the queue lock, we can be sure 56 // that the work thread is not exiting due to an empty queue and will process the work 57 // item we just added to it. If we weren't holding the lock we'd have to signal 58 // m_performWorkEvent to make sure the work item got picked up. 59 return; 60 } 61 } 62 63 queue->performWorkOnRegisteredWorkThread(); 64 } 30 65 31 66 void WorkQueue::registerHandle(HANDLE handle, PassOwnPtr<WorkItem> item) 32 67 { 68 RefPtr<WorkItemWin> itemWin = WorkItemWin::create(item, this); 69 33 70 // Add the item. 34 71 { 35 72 MutexLocker locker(m_handlesLock); 36 m_handles.set(handle, item .leakPtr());73 m_handles.set(handle, itemWin); 37 74 } 38 75 39 // Set the work event. 40 ::SetEvent(m_performWorkEvent); 76 // FIXME: We need to hold onto waitHandle so that we can unregister the wait later. 77 HANDLE waitHandle; 78 if (!::RegisterWaitForSingleObject(&waitHandle, handle, handleCallback, itemWin.get(), INFINITE, WT_EXECUTEDEFAULT)) { 79 DWORD error = ::GetLastError(); 80 ASSERT_NOT_REACHED(); 81 } 41 82 } 42 83 43 void * WorkQueue::workQueueThreadBody(void *context)84 void WorkQueue::eventCallback(void* context, BOOLEAN timerOrWaitFired) 44 85 { 45 static_cast<WorkQueue*>(context)->workQueueThreadBody(); 46 return 0; 86 ASSERT_ARG(context, context); 87 ASSERT_ARG(timerOrWaitFired, !timerOrWaitFired); 88 89 WorkQueue* queue = static_cast<WorkQueue*>(context); 90 91 if (!queue->tryRegisterAsWorkThread()) 92 return; 93 94 queue->performWorkOnRegisteredWorkThread(); 47 95 } 48 96 49 void WorkQueue:: workQueueThreadBody()97 void WorkQueue::performWorkOnRegisteredWorkThread() 50 98 { 51 while (true) { 52 Vector<HANDLE> handles; 53 { 54 // Copy the handles to our handles vector. 55 MutexLocker locker(m_handlesLock); 56 copyKeysToVector(m_handles, handles); 99 ASSERT(m_isWorkThreadRegistered); 100 101 bool isValid = true; 102 103 m_workItemQueueLock.lock(); 104 105 while (isValid && !m_workItemQueue.isEmpty()) { 106 Vector<RefPtr<WorkItemWin> > workItemQueue; 107 m_workItemQueue.swap(workItemQueue); 108 109 // Allow more work to be scheduled while we're not using the queue directly. 110 m_workItemQueueLock.unlock(); 111 for (size_t i = 0; i < workItemQueue.size(); ++i) { 112 MutexLocker locker(m_isValidMutex); 113 isValid = m_isValid; 114 if (!isValid) 115 break; 116 workItemQueue[i]->item()->execute(); 57 117 } 118 m_workItemQueueLock.lock(); 119 } 58 120 59 // Add the "perform work" event handle. 60 handles.append(m_performWorkEvent); 121 // One invariant we maintain is that any work scheduled while a work thread is registered will 122 // be handled by that work thread. Unregister as the work thread while the queue lock is still 123 // held so that no work can be scheduled while we're still registered. 124 unregisterAsWorkThread(); 61 125 62 ASSERT(handles.size() <= MAXIMUM_WAIT_OBJECTS); 63 64 // Now we wait. 65 DWORD result = ::WaitForMultipleObjects(handles.size(), handles.data(), FALSE, INFINITE); 66 if (result == WAIT_FAILED) { 67 DWORD error = ::GetLastError(); 68 ASSERT_NOT_REACHED(); 69 } 70 71 // The wait should never time out since we passed INFINITE for the timeout interval. 72 ASSERT(result != WAIT_TIMEOUT); 73 // We don't know how (or need) to handle abandoned mutexes yet. 74 ASSERT(result < WAIT_ABANDONED_0 || result >= WAIT_ABANDONED_0 + handles.size()); 75 76 if (result == handles.size() - 1) 77 performWork(); 78 else { 79 // FIXME: If we ever decide to support unregistering handles we would need to copy the hash map. 80 WorkItem* workItem; 81 HANDLE handle = handles[result]; 82 83 { 84 MutexLocker locker(m_handlesLock); 85 workItem = m_handles.get(handle); 86 } 87 88 // Execute the work item. 89 workItem->execute(); 90 } 91 92 // Check if this queue is invalid. 93 { 94 MutexLocker locker(m_isValidMutex); 95 if (!m_isValid) 96 break; 97 } 98 } 126 m_workItemQueueLock.unlock(); 99 127 } 100 128 101 129 void WorkQueue::platformInitialize(const char* name) 102 130 { 131 m_isWorkThreadRegistered = 0; 132 103 133 // Create our event. 104 m_performWorkEvent = ::CreateEvent (0, false, false, 0);134 m_performWorkEvent = ::CreateEventW(0, FALSE, FALSE, 0); 105 135 106 m_workQueueThread = createThread(&WorkQueue::workQueueThreadBody, this, name); 136 // FIXME: We need to hold onto waitHandle so that we can unregister the wait later. 137 HANDLE waitHandle; 138 if (!::RegisterWaitForSingleObject(&waitHandle, m_performWorkEvent, eventCallback, this, INFINITE, WT_EXECUTEDEFAULT)) { 139 DWORD error = ::GetLastError(); 140 ASSERT_NOT_REACHED(); 141 } 142 } 143 144 bool WorkQueue::tryRegisterAsWorkThread() 145 { 146 LONG result = ::InterlockedCompareExchange(&m_isWorkThreadRegistered, 1, 0); 147 ASSERT(!result || result == 1); 148 return !result; 149 } 150 151 void WorkQueue::unregisterAsWorkThread() 152 { 153 LONG result = ::InterlockedCompareExchange(&m_isWorkThreadRegistered, 0, 1); 154 ASSERT_UNUSED(result, result == 1); 107 155 } 108 156 … … 117 165 { 118 166 MutexLocker locker(m_workItemQueueLock); 119 m_workItemQueue.append(item.leakPtr());120 167 121 // Set the work event. 122 ::SetEvent(m_performWorkEvent); 168 m_workItemQueue.append(WorkItemWin::create(item, this)); 169 170 // Signal our event so that work thread will perform the work we just added. As an optimization, 171 // we avoid signaling the event if a work thread is already registered. This prevents multiple 172 // work threads from being spawned in most cases. (Note that when a work thread has been spawned 173 // but hasn't registered itself yet, m_isWorkThreadRegistered will be false and we'll end up 174 // spawning a second work thread here. But work thread registration process will ensure that 175 // only one thread actually ends up performing work.) 176 if (!m_isWorkThreadRegistered) 177 ::SetEvent(m_performWorkEvent); 123 178 } 124 125 void WorkQueue::performWork()126 {127 Vector<WorkItem*> workItemQueue;128 {129 MutexLocker locker(m_workItemQueueLock);130 m_workItemQueue.swap(workItemQueue);131 }132 133 for (size_t i = 0; i < workItemQueue.size(); ++i) {134 OwnPtr<WorkItem> item(workItemQueue[i]);135 136 MutexLocker locker(m_isValidMutex);137 if (m_isValid)138 item->execute();139 }140 }
Note: See TracChangeset
for help on using the changeset viewer.