Home | History | Annotate | Download | only in win
      1 /*
      2  * Copyright (C) 2010 Apple 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
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "RunLoop.h"
     28 
     29 #include "BinarySemaphore.h"
     30 #include "WorkItem.h"
     31 #include <wtf/CurrentTime.h>
     32 
     33 using namespace CoreIPC;
     34 using namespace std;
     35 
     36 static const UINT PerformWorkMessage = WM_USER + 1;
     37 static const LPWSTR kRunLoopMessageWindowClassName = L"RunLoopMessageWindow";
     38 
     39 LRESULT CALLBACK RunLoop::RunLoopWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
     40 {
     41     LONG_PTR longPtr = ::GetWindowLongPtr(hWnd, 0);
     42 
     43     if (RunLoop* runLoop = reinterpret_cast<RunLoop*>(longPtr))
     44         return runLoop->wndProc(hWnd, message, wParam, lParam);
     45 
     46     if (message == WM_CREATE) {
     47         LPCREATESTRUCT createStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
     48 
     49         // Associate the RunLoop with the window.
     50         ::SetWindowLongPtr(hWnd, 0, (LONG_PTR)createStruct->lpCreateParams);
     51         return 0;
     52     }
     53 
     54     return ::DefWindowProc(hWnd, message, wParam, lParam);
     55 }
     56 
     57 LRESULT RunLoop::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
     58 {
     59     switch (message) {
     60     case PerformWorkMessage:
     61         performWork();
     62         return 0;
     63     case WM_TIMER:
     64         RunLoop::TimerBase::timerFired(this, wParam);
     65         return 0;
     66     }
     67 
     68     return ::DefWindowProc(hWnd, message, wParam, lParam);
     69 }
     70 
     71 void RunLoop::run()
     72 {
     73     MSG message;
     74     while (BOOL result = ::GetMessage(&message, 0, 0, 0)) {
     75         if (result == -1)
     76             break;
     77         ::TranslateMessage(&message);
     78         ::DispatchMessage(&message);
     79     }
     80 }
     81 
     82 bool RunLoop::dispatchSentMessagesUntil(const Vector<HWND>& windows, CoreIPC::BinarySemaphore& semaphore, double absoluteTime)
     83 {
     84     if (windows.isEmpty())
     85         return semaphore.wait(absoluteTime);
     86 
     87     HANDLE handle = semaphore.event();
     88     DWORD handleCount = 1;
     89 
     90     while (true) {
     91         DWORD interval = absoluteTimeToWaitTimeoutInterval(absoluteTime);
     92         if (!interval) {
     93             // Consider the wait to have timed out, even if the semaphore is currently signaled.
     94             // This matches the WTF::ThreadCondition implementation of BinarySemaphore::wait.
     95             return false;
     96         }
     97 
     98         DWORD result = ::MsgWaitForMultipleObjectsEx(handleCount, &handle, interval, QS_SENDMESSAGE, 0);
     99         if (result == WAIT_OBJECT_0) {
    100             // The semaphore was signaled.
    101             return true;
    102         }
    103         if (result == WAIT_TIMEOUT) {
    104             // absoluteTime was reached.
    105             return false;
    106         }
    107         if (result == WAIT_OBJECT_0 + handleCount) {
    108             // One or more sent messages are available. Process sent messages for all the windows
    109             // we were given, since we don't have a way of knowing which window has available sent
    110             // messages.
    111             for (size_t i = 0; i < windows.size(); ++i) {
    112                 MSG message;
    113                 ::PeekMessageW(&message, windows[i], 0, 0, PM_NOREMOVE | PM_QS_SENDMESSAGE);
    114             }
    115             continue;
    116         }
    117         ASSERT_WITH_MESSAGE(result != WAIT_FAILED, "::MsgWaitForMultipleObjectsEx failed with error %lu", ::GetLastError());
    118         ASSERT_WITH_MESSAGE(false, "::MsgWaitForMultipleObjectsEx returned unexpected result %lu", result);
    119         return false;
    120     }
    121 }
    122 
    123 void RunLoop::stop()
    124 {
    125     ::PostQuitMessage(0);
    126 }
    127 
    128 bool RunLoop::registerRunLoopMessageWindowClass()
    129 {
    130     // FIXME: This really only needs to be called once.
    131 
    132     WNDCLASSEX windowClass = { 0 };
    133     windowClass.cbSize          = sizeof(windowClass);
    134     windowClass.lpfnWndProc     = RunLoop::RunLoopWndProc;
    135     windowClass.cbWndExtra      = sizeof(RunLoop*);
    136     windowClass.lpszClassName   = kRunLoopMessageWindowClassName;
    137 
    138     return !!::RegisterClassEx(&windowClass);
    139 }
    140 
    141 RunLoop::RunLoop()
    142 {
    143     registerRunLoopMessageWindowClass();
    144 
    145     m_runLoopMessageWindow = ::CreateWindow(kRunLoopMessageWindowClassName, 0, 0,
    146                                             CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
    147                                             HWND_MESSAGE, 0, 0, this);
    148     ASSERT(::IsWindow(m_runLoopMessageWindow));
    149 }
    150 
    151 RunLoop::~RunLoop()
    152 {
    153     // FIXME: Tear down the work item queue here.
    154 }
    155 
    156 void RunLoop::wakeUp()
    157 {
    158     // FIXME: No need to wake up the run loop if we've already called scheduleWork
    159     // before the run loop has had the time to respond.
    160     ::PostMessage(m_runLoopMessageWindow, PerformWorkMessage, reinterpret_cast<WPARAM>(this), 0);
    161 }
    162 
    163 // RunLoop::Timer
    164 
    165 void RunLoop::TimerBase::timerFired(RunLoop* runLoop, uint64_t ID)
    166 {
    167     TimerMap::iterator it = runLoop->m_activeTimers.find(ID);
    168     if (it == runLoop->m_activeTimers.end()) {
    169         // The timer must have been stopped after the WM_TIMER message was posted to the message queue.
    170         return;
    171     }
    172 
    173     TimerBase* timer = it->second;
    174 
    175     if (!timer->m_isRepeating) {
    176         runLoop->m_activeTimers.remove(it);
    177         ::KillTimer(runLoop->m_runLoopMessageWindow, ID);
    178     }
    179 
    180     timer->fired();
    181 }
    182 
    183 static uint64_t generateTimerID()
    184 {
    185     static uint64_t uniqueTimerID = 1;
    186     return uniqueTimerID++;
    187 }
    188 
    189 RunLoop::TimerBase::TimerBase(RunLoop* runLoop)
    190     : m_runLoop(runLoop)
    191     , m_ID(generateTimerID())
    192     , m_isRepeating(false)
    193 {
    194 }
    195 
    196 RunLoop::TimerBase::~TimerBase()
    197 {
    198     stop();
    199 }
    200 
    201 void RunLoop::TimerBase::start(double nextFireInterval, bool repeat)
    202 {
    203     m_isRepeating = repeat;
    204     m_runLoop->m_activeTimers.set(m_ID, this);
    205     ::SetTimer(m_runLoop->m_runLoopMessageWindow, m_ID, nextFireInterval * 1000, 0);
    206 }
    207 
    208 void RunLoop::TimerBase::stop()
    209 {
    210     TimerMap::iterator it = m_runLoop->m_activeTimers.find(m_ID);
    211     if (it == m_runLoop->m_activeTimers.end())
    212         return;
    213 
    214     m_runLoop->m_activeTimers.remove(it);
    215     ::KillTimer(m_runLoop->m_runLoopMessageWindow, m_ID);
    216 }
    217 
    218 bool RunLoop::TimerBase::isActive() const
    219 {
    220     return m_runLoop->m_activeTimers.contains(m_ID);
    221 }
    222