|
1 /* |
|
2 * Copyright (C) 2007, 2008 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 * |
|
8 * 1. Redistributions of source code must retain the above copyright |
|
9 * notice, this list of conditions and the following disclaimer. |
|
10 * 2. Redistributions in binary form must reproduce the above copyright |
|
11 * notice, this list of conditions and the following disclaimer in the |
|
12 * documentation and/or other materials provided with the distribution. |
|
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
|
14 * its contributors may be used to endorse or promote products derived |
|
15 * from this software without specific prior written permission. |
|
16 * |
|
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
|
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
|
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
27 */ |
|
28 |
|
29 #include "config.h" |
|
30 #include "MainThread.h" |
|
31 |
|
32 #include "CurrentTime.h" |
|
33 #include "Deque.h" |
|
34 #include "StdLibExtras.h" |
|
35 #include "Threading.h" |
|
36 |
|
37 #if PLATFORM(CHROMIUM) |
|
38 #error Chromium uses a different main thread implementation |
|
39 #endif |
|
40 |
|
41 namespace WTF { |
|
42 |
|
43 struct FunctionWithContext { |
|
44 MainThreadFunction* function; |
|
45 void* context; |
|
46 ThreadCondition* syncFlag; |
|
47 |
|
48 FunctionWithContext(MainThreadFunction* function = 0, void* context = 0, ThreadCondition* syncFlag = 0) |
|
49 : function(function) |
|
50 , context(context) |
|
51 , syncFlag(syncFlag) |
|
52 { |
|
53 } |
|
54 bool operator == (const FunctionWithContext& o) |
|
55 { |
|
56 return function == o.function |
|
57 && context == o.context |
|
58 && syncFlag == o.syncFlag; |
|
59 } |
|
60 }; |
|
61 |
|
62 class FunctionWithContextFinder { |
|
63 public: |
|
64 FunctionWithContextFinder(const FunctionWithContext& m) : m(m) {} |
|
65 bool operator() (FunctionWithContext& o) { return o == m; } |
|
66 FunctionWithContext m; |
|
67 }; |
|
68 |
|
69 |
|
70 typedef Deque<FunctionWithContext> FunctionQueue; |
|
71 |
|
72 static bool callbacksPaused; // This global variable is only accessed from main thread. |
|
73 #if !PLATFORM(MAC) && !PLATFORM(QT) |
|
74 static ThreadIdentifier mainThreadIdentifier; |
|
75 #endif |
|
76 |
|
77 static Mutex& mainThreadFunctionQueueMutex() |
|
78 { |
|
79 DEFINE_STATIC_LOCAL(Mutex, staticMutex, ()); |
|
80 return staticMutex; |
|
81 } |
|
82 |
|
83 static FunctionQueue& functionQueue() |
|
84 { |
|
85 DEFINE_STATIC_LOCAL(FunctionQueue, staticFunctionQueue, ()); |
|
86 return staticFunctionQueue; |
|
87 } |
|
88 |
|
89 |
|
90 #if !PLATFORM(MAC) |
|
91 |
|
92 void initializeMainThread() |
|
93 { |
|
94 static bool initializedMainThread; |
|
95 if (initializedMainThread) |
|
96 return; |
|
97 initializedMainThread = true; |
|
98 |
|
99 #if !PLATFORM(QT) |
|
100 mainThreadIdentifier = currentThread(); |
|
101 #endif |
|
102 |
|
103 mainThreadFunctionQueueMutex(); |
|
104 initializeMainThreadPlatform(); |
|
105 } |
|
106 |
|
107 #else |
|
108 |
|
109 static pthread_once_t initializeMainThreadKeyOnce = PTHREAD_ONCE_INIT; |
|
110 |
|
111 static void initializeMainThreadOnce() |
|
112 { |
|
113 mainThreadFunctionQueueMutex(); |
|
114 initializeMainThreadPlatform(); |
|
115 } |
|
116 |
|
117 void initializeMainThread() |
|
118 { |
|
119 pthread_once(&initializeMainThreadKeyOnce, initializeMainThreadOnce); |
|
120 } |
|
121 |
|
122 static void initializeMainThreadToProcessMainThreadOnce() |
|
123 { |
|
124 mainThreadFunctionQueueMutex(); |
|
125 initializeMainThreadToProcessMainThreadPlatform(); |
|
126 } |
|
127 |
|
128 void initializeMainThreadToProcessMainThread() |
|
129 { |
|
130 pthread_once(&initializeMainThreadKeyOnce, initializeMainThreadToProcessMainThreadOnce); |
|
131 } |
|
132 #endif |
|
133 |
|
134 // 0.1 sec delays in UI is approximate threshold when they become noticeable. Have a limit that's half of that. |
|
135 static const double maxRunLoopSuspensionTime = 0.05; |
|
136 |
|
137 void dispatchFunctionsFromMainThread() |
|
138 { |
|
139 ASSERT(isMainThread()); |
|
140 |
|
141 if (callbacksPaused) |
|
142 return; |
|
143 |
|
144 double startTime = currentTime(); |
|
145 |
|
146 FunctionWithContext invocation; |
|
147 while (true) { |
|
148 { |
|
149 MutexLocker locker(mainThreadFunctionQueueMutex()); |
|
150 if (!functionQueue().size()) |
|
151 break; |
|
152 invocation = functionQueue().takeFirst(); |
|
153 } |
|
154 |
|
155 invocation.function(invocation.context); |
|
156 if (invocation.syncFlag) |
|
157 invocation.syncFlag->signal(); |
|
158 |
|
159 // If we are running accumulated functions for too long so UI may become unresponsive, we need to |
|
160 // yield so the user input can be processed. Otherwise user may not be able to even close the window. |
|
161 // This code has effect only in case the scheduleDispatchFunctionsOnMainThread() is implemented in a way that |
|
162 // allows input events to be processed before we are back here. |
|
163 if (currentTime() - startTime > maxRunLoopSuspensionTime) { |
|
164 scheduleDispatchFunctionsOnMainThread(); |
|
165 break; |
|
166 } |
|
167 } |
|
168 } |
|
169 |
|
170 void callOnMainThread(MainThreadFunction* function, void* context) |
|
171 { |
|
172 ASSERT(function); |
|
173 bool needToSchedule = false; |
|
174 { |
|
175 MutexLocker locker(mainThreadFunctionQueueMutex()); |
|
176 needToSchedule = functionQueue().size() == 0; |
|
177 functionQueue().append(FunctionWithContext(function, context)); |
|
178 } |
|
179 if (needToSchedule) |
|
180 scheduleDispatchFunctionsOnMainThread(); |
|
181 } |
|
182 |
|
183 void callOnMainThreadAndWait(MainThreadFunction* function, void* context) |
|
184 { |
|
185 ASSERT(function); |
|
186 |
|
187 if (isMainThread()) { |
|
188 function(context); |
|
189 return; |
|
190 } |
|
191 |
|
192 ThreadCondition syncFlag; |
|
193 Mutex& functionQueueMutex = mainThreadFunctionQueueMutex(); |
|
194 MutexLocker locker(functionQueueMutex); |
|
195 functionQueue().append(FunctionWithContext(function, context, &syncFlag)); |
|
196 if (functionQueue().size() == 1) |
|
197 scheduleDispatchFunctionsOnMainThread(); |
|
198 syncFlag.wait(functionQueueMutex); |
|
199 } |
|
200 |
|
201 void cancelCallOnMainThread(MainThreadFunction* function, void* context) |
|
202 { |
|
203 ASSERT(function); |
|
204 |
|
205 MutexLocker locker(mainThreadFunctionQueueMutex()); |
|
206 |
|
207 FunctionWithContextFinder pred(FunctionWithContext(function, context)); |
|
208 |
|
209 while (true) { |
|
210 // We must redefine 'i' each pass, because the itererator's operator= |
|
211 // requires 'this' to be valid, and remove() invalidates all iterators |
|
212 FunctionQueue::iterator i(functionQueue().findIf(pred)); |
|
213 if (i == functionQueue().end()) |
|
214 break; |
|
215 functionQueue().remove(i); |
|
216 } |
|
217 } |
|
218 |
|
219 void setMainThreadCallbacksPaused(bool paused) |
|
220 { |
|
221 ASSERT(isMainThread()); |
|
222 |
|
223 if (callbacksPaused == paused) |
|
224 return; |
|
225 |
|
226 callbacksPaused = paused; |
|
227 |
|
228 if (!callbacksPaused) |
|
229 scheduleDispatchFunctionsOnMainThread(); |
|
230 } |
|
231 |
|
232 #if !PLATFORM(MAC) && !PLATFORM(QT) && !PLATFORM(BREWMP) |
|
233 bool isMainThread() |
|
234 { |
|
235 return currentThread() == mainThreadIdentifier; |
|
236 } |
|
237 #endif |
|
238 |
|
239 } // namespace WTF |