|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "nsThreadUtils.h" |
|
8 #include "mozilla/Attributes.h" |
|
9 #include "mozilla/Likely.h" |
|
10 |
|
11 #ifdef MOZILLA_INTERNAL_API |
|
12 # include "nsThreadManager.h" |
|
13 #else |
|
14 # include "nsXPCOMCIDInternal.h" |
|
15 # include "nsIThreadManager.h" |
|
16 # include "nsServiceManagerUtils.h" |
|
17 #endif |
|
18 |
|
19 #ifdef XP_WIN |
|
20 #include <windows.h> |
|
21 #include "mozilla/WindowsVersion.h" |
|
22 using mozilla::IsVistaOrLater; |
|
23 #elif defined(XP_MACOSX) |
|
24 #include <sys/resource.h> |
|
25 #endif |
|
26 |
|
27 #include <pratom.h> |
|
28 #include <prthread.h> |
|
29 |
|
30 #ifndef XPCOM_GLUE_AVOID_NSPR |
|
31 |
|
32 NS_IMPL_ISUPPORTS(nsRunnable, nsIRunnable) |
|
33 |
|
34 NS_IMETHODIMP |
|
35 nsRunnable::Run() |
|
36 { |
|
37 // Do nothing |
|
38 return NS_OK; |
|
39 } |
|
40 |
|
41 NS_IMPL_ISUPPORTS(nsCancelableRunnable, nsICancelableRunnable, |
|
42 nsIRunnable) |
|
43 |
|
44 NS_IMETHODIMP |
|
45 nsCancelableRunnable::Run() |
|
46 { |
|
47 // Do nothing |
|
48 return NS_OK; |
|
49 } |
|
50 |
|
51 NS_IMETHODIMP |
|
52 nsCancelableRunnable::Cancel() |
|
53 { |
|
54 // Do nothing |
|
55 return NS_OK; |
|
56 } |
|
57 |
|
58 #endif // XPCOM_GLUE_AVOID_NSPR |
|
59 |
|
60 //----------------------------------------------------------------------------- |
|
61 |
|
62 NS_METHOD |
|
63 NS_NewThread(nsIThread **result, nsIRunnable *event, uint32_t stackSize) |
|
64 { |
|
65 nsCOMPtr<nsIThread> thread; |
|
66 #ifdef MOZILLA_INTERNAL_API |
|
67 nsresult rv = nsThreadManager::get()-> |
|
68 nsThreadManager::NewThread(0, stackSize, getter_AddRefs(thread)); |
|
69 #else |
|
70 nsresult rv; |
|
71 nsCOMPtr<nsIThreadManager> mgr = |
|
72 do_GetService(NS_THREADMANAGER_CONTRACTID, &rv); |
|
73 if (NS_WARN_IF(NS_FAILED(rv))) |
|
74 return rv; |
|
75 |
|
76 rv = mgr->NewThread(0, stackSize, getter_AddRefs(thread)); |
|
77 #endif |
|
78 if (NS_WARN_IF(NS_FAILED(rv))) |
|
79 return rv; |
|
80 |
|
81 if (event) { |
|
82 rv = thread->Dispatch(event, NS_DISPATCH_NORMAL); |
|
83 if (NS_WARN_IF(NS_FAILED(rv))) |
|
84 return rv; |
|
85 } |
|
86 |
|
87 *result = nullptr; |
|
88 thread.swap(*result); |
|
89 return NS_OK; |
|
90 } |
|
91 |
|
92 NS_METHOD |
|
93 NS_GetCurrentThread(nsIThread **result) |
|
94 { |
|
95 #ifdef MOZILLA_INTERNAL_API |
|
96 return nsThreadManager::get()->nsThreadManager::GetCurrentThread(result); |
|
97 #else |
|
98 nsresult rv; |
|
99 nsCOMPtr<nsIThreadManager> mgr = |
|
100 do_GetService(NS_THREADMANAGER_CONTRACTID, &rv); |
|
101 if (NS_WARN_IF(NS_FAILED(rv))) |
|
102 return rv; |
|
103 return mgr->GetCurrentThread(result); |
|
104 #endif |
|
105 } |
|
106 |
|
107 NS_METHOD |
|
108 NS_GetMainThread(nsIThread **result) |
|
109 { |
|
110 #ifdef MOZILLA_INTERNAL_API |
|
111 return nsThreadManager::get()->nsThreadManager::GetMainThread(result); |
|
112 #else |
|
113 nsresult rv; |
|
114 nsCOMPtr<nsIThreadManager> mgr = |
|
115 do_GetService(NS_THREADMANAGER_CONTRACTID, &rv); |
|
116 if (NS_WARN_IF(NS_FAILED(rv))) |
|
117 return rv; |
|
118 return mgr->GetMainThread(result); |
|
119 #endif |
|
120 } |
|
121 |
|
122 #if defined(MOZILLA_INTERNAL_API) && defined(XP_WIN) |
|
123 extern DWORD gTLSThreadIDIndex; |
|
124 bool |
|
125 NS_IsMainThread() |
|
126 { |
|
127 return TlsGetValue(gTLSThreadIDIndex) == (void*) mozilla::threads::Main; |
|
128 } |
|
129 #elif defined(MOZILLA_INTERNAL_API) && defined(NS_TLS) |
|
130 #ifdef MOZ_ASAN |
|
131 // Temporary workaround, see bug 895845 |
|
132 bool NS_IsMainThread() |
|
133 { |
|
134 return gTLSThreadID == mozilla::threads::Main; |
|
135 } |
|
136 #else |
|
137 // NS_IsMainThread() is defined inline in MainThreadUtils.h |
|
138 #endif |
|
139 #else |
|
140 #ifdef MOZILLA_INTERNAL_API |
|
141 bool NS_IsMainThread() |
|
142 { |
|
143 bool result = false; |
|
144 nsThreadManager::get()->nsThreadManager::GetIsMainThread(&result); |
|
145 return bool(result); |
|
146 } |
|
147 #else |
|
148 bool NS_IsMainThread() |
|
149 { |
|
150 bool result = false; |
|
151 nsCOMPtr<nsIThreadManager> mgr = |
|
152 do_GetService(NS_THREADMANAGER_CONTRACTID); |
|
153 if (mgr) |
|
154 mgr->GetIsMainThread(&result); |
|
155 return bool(result); |
|
156 } |
|
157 #endif |
|
158 #endif |
|
159 |
|
160 NS_METHOD |
|
161 NS_DispatchToCurrentThread(nsIRunnable *event) |
|
162 { |
|
163 #ifdef MOZILLA_INTERNAL_API |
|
164 nsIThread *thread = NS_GetCurrentThread(); |
|
165 if (!thread) { return NS_ERROR_UNEXPECTED; } |
|
166 #else |
|
167 nsCOMPtr<nsIThread> thread; |
|
168 nsresult rv = NS_GetCurrentThread(getter_AddRefs(thread)); |
|
169 if (NS_WARN_IF(NS_FAILED(rv))) |
|
170 return rv; |
|
171 #endif |
|
172 return thread->Dispatch(event, NS_DISPATCH_NORMAL); |
|
173 } |
|
174 |
|
175 NS_METHOD |
|
176 NS_DispatchToMainThread(nsIRunnable *event, uint32_t dispatchFlags) |
|
177 { |
|
178 nsCOMPtr<nsIThread> thread; |
|
179 nsresult rv = NS_GetMainThread(getter_AddRefs(thread)); |
|
180 if (NS_WARN_IF(NS_FAILED(rv))) |
|
181 return rv; |
|
182 return thread->Dispatch(event, dispatchFlags); |
|
183 } |
|
184 |
|
185 #ifndef XPCOM_GLUE_AVOID_NSPR |
|
186 NS_METHOD |
|
187 NS_ProcessPendingEvents(nsIThread *thread, PRIntervalTime timeout) |
|
188 { |
|
189 nsresult rv = NS_OK; |
|
190 |
|
191 #ifdef MOZILLA_INTERNAL_API |
|
192 if (!thread) { |
|
193 thread = NS_GetCurrentThread(); |
|
194 if (NS_WARN_IF(!thread)) |
|
195 return NS_ERROR_UNEXPECTED; |
|
196 } |
|
197 #else |
|
198 nsCOMPtr<nsIThread> current; |
|
199 if (!thread) { |
|
200 rv = NS_GetCurrentThread(getter_AddRefs(current)); |
|
201 if (NS_WARN_IF(NS_FAILED(rv))) |
|
202 return rv; |
|
203 thread = current.get(); |
|
204 } |
|
205 #endif |
|
206 |
|
207 PRIntervalTime start = PR_IntervalNow(); |
|
208 for (;;) { |
|
209 bool processedEvent; |
|
210 rv = thread->ProcessNextEvent(false, &processedEvent); |
|
211 if (NS_FAILED(rv) || !processedEvent) |
|
212 break; |
|
213 if (PR_IntervalNow() - start > timeout) |
|
214 break; |
|
215 } |
|
216 return rv; |
|
217 } |
|
218 #endif // XPCOM_GLUE_AVOID_NSPR |
|
219 |
|
220 inline bool |
|
221 hasPendingEvents(nsIThread *thread) |
|
222 { |
|
223 bool val; |
|
224 return NS_SUCCEEDED(thread->HasPendingEvents(&val)) && val; |
|
225 } |
|
226 |
|
227 bool |
|
228 NS_HasPendingEvents(nsIThread *thread) |
|
229 { |
|
230 if (!thread) { |
|
231 #ifndef MOZILLA_INTERNAL_API |
|
232 nsCOMPtr<nsIThread> current; |
|
233 NS_GetCurrentThread(getter_AddRefs(current)); |
|
234 return hasPendingEvents(current); |
|
235 #else |
|
236 thread = NS_GetCurrentThread(); |
|
237 if (NS_WARN_IF(!thread)) |
|
238 return false; |
|
239 #endif |
|
240 } |
|
241 return hasPendingEvents(thread); |
|
242 } |
|
243 |
|
244 bool |
|
245 NS_ProcessNextEvent(nsIThread *thread, bool mayWait) |
|
246 { |
|
247 #ifdef MOZILLA_INTERNAL_API |
|
248 if (!thread) { |
|
249 thread = NS_GetCurrentThread(); |
|
250 if (NS_WARN_IF(!thread)) |
|
251 return false; |
|
252 } |
|
253 #else |
|
254 nsCOMPtr<nsIThread> current; |
|
255 if (!thread) { |
|
256 NS_GetCurrentThread(getter_AddRefs(current)); |
|
257 if (NS_WARN_IF(!current)) |
|
258 return false; |
|
259 thread = current.get(); |
|
260 } |
|
261 #endif |
|
262 bool val; |
|
263 return NS_SUCCEEDED(thread->ProcessNextEvent(mayWait, &val)) && val; |
|
264 } |
|
265 |
|
266 #ifndef XPCOM_GLUE_AVOID_NSPR |
|
267 |
|
268 namespace { |
|
269 |
|
270 class nsNameThreadRunnable MOZ_FINAL : public nsIRunnable |
|
271 { |
|
272 public: |
|
273 nsNameThreadRunnable(const nsACString &name) : mName(name) { } |
|
274 |
|
275 NS_DECL_THREADSAFE_ISUPPORTS |
|
276 NS_DECL_NSIRUNNABLE |
|
277 |
|
278 protected: |
|
279 const nsCString mName; |
|
280 }; |
|
281 |
|
282 NS_IMPL_ISUPPORTS(nsNameThreadRunnable, nsIRunnable) |
|
283 |
|
284 NS_IMETHODIMP |
|
285 nsNameThreadRunnable::Run() |
|
286 { |
|
287 PR_SetCurrentThreadName(mName.BeginReading()); |
|
288 return NS_OK; |
|
289 } |
|
290 |
|
291 } // anonymous namespace |
|
292 |
|
293 void |
|
294 NS_SetThreadName(nsIThread *thread, const nsACString &name) |
|
295 { |
|
296 if (!thread) |
|
297 return; |
|
298 |
|
299 thread->Dispatch(new nsNameThreadRunnable(name), |
|
300 nsIEventTarget::DISPATCH_NORMAL); |
|
301 } |
|
302 |
|
303 #else // !XPCOM_GLUE_AVOID_NSPR |
|
304 |
|
305 void |
|
306 NS_SetThreadName(nsIThread *thread, const nsACString &name) |
|
307 { |
|
308 // No NSPR, no love. |
|
309 } |
|
310 |
|
311 #endif |
|
312 |
|
313 #ifdef MOZILLA_INTERNAL_API |
|
314 nsIThread * |
|
315 NS_GetCurrentThread() { |
|
316 return nsThreadManager::get()->GetCurrentThread(); |
|
317 } |
|
318 #endif |
|
319 |
|
320 // nsThreadPoolNaming |
|
321 void |
|
322 nsThreadPoolNaming::SetThreadPoolName(const nsACString & aPoolName, |
|
323 nsIThread * aThread) |
|
324 { |
|
325 nsCString name(aPoolName); |
|
326 name.Append(NS_LITERAL_CSTRING(" #")); |
|
327 name.AppendInt(++mCounter, 10); // The counter is declared as volatile |
|
328 |
|
329 if (aThread) { |
|
330 // Set on the target thread |
|
331 NS_SetThreadName(aThread, name); |
|
332 } |
|
333 else { |
|
334 // Set on the current thread |
|
335 PR_SetCurrentThreadName(name.BeginReading()); |
|
336 } |
|
337 } |
|
338 |
|
339 // nsAutoLowPriorityIO |
|
340 nsAutoLowPriorityIO::nsAutoLowPriorityIO() |
|
341 { |
|
342 #if defined(XP_WIN) |
|
343 lowIOPrioritySet = IsVistaOrLater() && |
|
344 SetThreadPriority(GetCurrentThread(), |
|
345 THREAD_MODE_BACKGROUND_BEGIN); |
|
346 #elif defined(XP_MACOSX) |
|
347 oldPriority = getiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD); |
|
348 lowIOPrioritySet = oldPriority != -1 && |
|
349 setiopolicy_np(IOPOL_TYPE_DISK, |
|
350 IOPOL_SCOPE_THREAD, |
|
351 IOPOL_THROTTLE) != -1; |
|
352 #else |
|
353 lowIOPrioritySet = false; |
|
354 #endif |
|
355 } |
|
356 |
|
357 nsAutoLowPriorityIO::~nsAutoLowPriorityIO() |
|
358 { |
|
359 #if defined(XP_WIN) |
|
360 if (MOZ_LIKELY(lowIOPrioritySet)) { |
|
361 // On Windows the old thread priority is automatically restored |
|
362 SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END); |
|
363 } |
|
364 #elif defined(XP_MACOSX) |
|
365 if (MOZ_LIKELY(lowIOPrioritySet)) { |
|
366 setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, oldPriority); |
|
367 } |
|
368 #endif |
|
369 } |
|
370 |