Make every port implement MainThreadSharedTimer instead of using global functions
[WebKit-https.git] / Source / WebCore / platform / win / MainThreadSharedTimerWin.cpp
1 /*
2  * Copyright (C) 2006 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "MainThreadSharedTimer.h"
28
29 #include "Page.h"
30 #include "Settings.h"
31 #include "WebCoreInstanceHandle.h"
32 #include "Widget.h"
33 #include <wtf/Assertions.h>
34 #include <wtf/CurrentTime.h>
35 #include <wtf/WindowsExtras.h>
36
37 #include <mmsystem.h>
38
39 // These aren't in winuser.h with the MSVS 2003 Platform SDK, 
40 // so use default values in that case.
41 #ifndef USER_TIMER_MINIMUM
42 #define USER_TIMER_MINIMUM 0x0000000A
43 #endif
44
45 #ifndef USER_TIMER_MAXIMUM
46 #define USER_TIMER_MAXIMUM 0x7FFFFFFF
47 #endif
48
49 #ifndef QS_RAWINPUT
50 #define QS_RAWINPUT         0x0400
51 #endif
52
53 namespace WebCore {
54
55 static UINT timerID;
56
57 static HANDLE timer;
58 static HWND timerWindowHandle = 0;
59 const LPCWSTR kTimerWindowClassName = L"TimerWindowClass";
60
61 static UINT timerFiredMessage = 0;
62 static HANDLE timerQueue;
63 static bool highResTimerActive;
64 static bool processingCustomTimerMessage = false;
65 static LONG pendingTimers;
66
67 const int timerResolution = 1; // To improve timer resolution, we call timeBeginPeriod/timeEndPeriod with this value to increase timer resolution to 1ms.
68 const int highResolutionThresholdMsec = 16; // Only activate high-res timer for sub-16ms timers (Windows can fire timers at 16ms intervals without changing the system resolution).
69 const int stopHighResTimerInMsec = 300; // Stop high-res timer after 0.3 seconds to lessen power consumption (we don't use a smaller time since oscillating between high and low resolution breaks timer accuracy on XP).
70
71 enum {
72     sharedTimerID = 1000,
73     endHighResTimerID = 1001,
74 };
75
76 LRESULT CALLBACK TimerWindowWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
77 {
78     if (message == WM_TIMER) {
79         if (wParam == sharedTimerID) {
80             KillTimer(timerWindowHandle, sharedTimerID);
81             MainThreadSharedTimer::singleton().fired();
82         } else if (wParam == endHighResTimerID) {
83             KillTimer(timerWindowHandle, endHighResTimerID);
84             highResTimerActive = false;
85             timeEndPeriod(timerResolution);
86         }
87     } else if (message == timerFiredMessage) {
88         InterlockedExchange(&pendingTimers, 0);
89         processingCustomTimerMessage = true;
90         MainThreadSharedTimer::singleton().fired();
91         processingCustomTimerMessage = false;
92     } else
93         return DefWindowProc(hWnd, message, wParam, lParam);
94
95     return 0;
96 }
97
98 static void initializeOffScreenTimerWindow()
99 {
100     if (timerWindowHandle)
101         return;
102
103     WNDCLASSEX wcex;
104     memset(&wcex, 0, sizeof(WNDCLASSEX));
105     wcex.cbSize = sizeof(WNDCLASSEX);
106
107     wcex.lpfnWndProc    = TimerWindowWndProc;
108     wcex.hInstance      = WebCore::instanceHandle();
109     wcex.lpszClassName  = kTimerWindowClassName;
110     RegisterClassEx(&wcex);
111
112     timerWindowHandle = CreateWindow(kTimerWindowClassName, 0, 0,
113        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, HWND_MESSAGE, 0, WebCore::instanceHandle(), 0);
114
115     timerFiredMessage = RegisterWindowMessage(L"com.apple.WebKit.TimerFired");
116 }
117
118 static void NTAPI queueTimerProc(PVOID, BOOLEAN)
119 {
120     if (InterlockedIncrement(&pendingTimers) == 1)
121         PostMessage(timerWindowHandle, timerFiredMessage, 0, 0);
122 }
123
124 void MainThreadSharedTimer::setFireInterval(double interval)
125 {
126     ASSERT(m_firedFunction);
127
128     unsigned intervalInMS;
129     interval *= 1000;
130     if (interval > USER_TIMER_MAXIMUM)
131         intervalInMS = USER_TIMER_MAXIMUM;
132     else
133         intervalInMS = static_cast<unsigned>(interval);
134
135     initializeOffScreenTimerWindow();
136     bool timerSet = false;
137
138     if (Settings::shouldUseHighResolutionTimers()) {
139         if (interval < highResolutionThresholdMsec) {
140             if (!highResTimerActive) {
141                 highResTimerActive = true;
142                 timeBeginPeriod(timerResolution);
143             }
144             SetTimer(timerWindowHandle, endHighResTimerID, stopHighResTimerInMsec, 0);
145         }
146
147         DWORD queueStatus = LOWORD(GetQueueStatus(QS_PAINT | QS_MOUSEBUTTON | QS_KEY | QS_RAWINPUT));
148
149         // Win32 has a tri-level queue with application messages > user input > WM_PAINT/WM_TIMER.
150
151         // If the queue doesn't contains input events, we use a higher priorty timer event posting mechanism.
152         if (!(queueStatus & (QS_MOUSEBUTTON | QS_KEY | QS_RAWINPUT))) {
153             if (intervalInMS < USER_TIMER_MINIMUM && !processingCustomTimerMessage && !(queueStatus & QS_PAINT)) {
154                 // Call PostMessage immediately if the timer is already expired, unless a paint is pending.
155                 // (we prioritize paints over timers)
156                 if (InterlockedIncrement(&pendingTimers) == 1)
157                     PostMessage(timerWindowHandle, timerFiredMessage, 0, 0);
158                 timerSet = true;
159             } else {
160                 // Otherwise, delay the PostMessage via a CreateTimerQueueTimer
161                 if (!timerQueue)
162                     timerQueue = CreateTimerQueue();
163                 if (timer)
164                     DeleteTimerQueueTimer(timerQueue, timer, 0);
165                 timerSet = CreateTimerQueueTimer(&timer, timerQueue, queueTimerProc, 0, intervalInMS, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE);
166             }
167         }
168     }
169
170     if (timerSet) {
171         if (timerID) {
172             KillTimer(timerWindowHandle, timerID);
173             timerID = 0;
174         }
175     } else {
176         timerID = SetTimer(timerWindowHandle, sharedTimerID, intervalInMS, 0);
177         timer = 0;
178     }
179 }
180
181 void MainThreadSharedTimer::stop()
182 {
183     if (timerQueue && timer) {
184         DeleteTimerQueueTimer(timerQueue, timer, 0);
185         timer = 0;
186     }
187
188     if (timerID) {
189         KillTimer(timerWindowHandle, timerID);
190         timerID = 0;
191     }
192 }
193
194 void MainThreadSharedTimer::invalidate()
195 {
196 }
197
198 }