Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
7 #include "nsThreadUtils.h"
8 #include "mozilla/Attributes.h"
9 #include "mozilla/Likely.h"
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
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
27 #include <pratom.h>
28 #include <prthread.h>
30 #ifndef XPCOM_GLUE_AVOID_NSPR
32 NS_IMPL_ISUPPORTS(nsRunnable, nsIRunnable)
34 NS_IMETHODIMP
35 nsRunnable::Run()
36 {
37 // Do nothing
38 return NS_OK;
39 }
41 NS_IMPL_ISUPPORTS(nsCancelableRunnable, nsICancelableRunnable,
42 nsIRunnable)
44 NS_IMETHODIMP
45 nsCancelableRunnable::Run()
46 {
47 // Do nothing
48 return NS_OK;
49 }
51 NS_IMETHODIMP
52 nsCancelableRunnable::Cancel()
53 {
54 // Do nothing
55 return NS_OK;
56 }
58 #endif // XPCOM_GLUE_AVOID_NSPR
60 //-----------------------------------------------------------------------------
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;
76 rv = mgr->NewThread(0, stackSize, getter_AddRefs(thread));
77 #endif
78 if (NS_WARN_IF(NS_FAILED(rv)))
79 return rv;
81 if (event) {
82 rv = thread->Dispatch(event, NS_DISPATCH_NORMAL);
83 if (NS_WARN_IF(NS_FAILED(rv)))
84 return rv;
85 }
87 *result = nullptr;
88 thread.swap(*result);
89 return NS_OK;
90 }
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 }
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 }
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
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 }
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 }
185 #ifndef XPCOM_GLUE_AVOID_NSPR
186 NS_METHOD
187 NS_ProcessPendingEvents(nsIThread *thread, PRIntervalTime timeout)
188 {
189 nsresult rv = NS_OK;
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
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
220 inline bool
221 hasPendingEvents(nsIThread *thread)
222 {
223 bool val;
224 return NS_SUCCEEDED(thread->HasPendingEvents(&val)) && val;
225 }
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 }
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 }
266 #ifndef XPCOM_GLUE_AVOID_NSPR
268 namespace {
270 class nsNameThreadRunnable MOZ_FINAL : public nsIRunnable
271 {
272 public:
273 nsNameThreadRunnable(const nsACString &name) : mName(name) { }
275 NS_DECL_THREADSAFE_ISUPPORTS
276 NS_DECL_NSIRUNNABLE
278 protected:
279 const nsCString mName;
280 };
282 NS_IMPL_ISUPPORTS(nsNameThreadRunnable, nsIRunnable)
284 NS_IMETHODIMP
285 nsNameThreadRunnable::Run()
286 {
287 PR_SetCurrentThreadName(mName.BeginReading());
288 return NS_OK;
289 }
291 } // anonymous namespace
293 void
294 NS_SetThreadName(nsIThread *thread, const nsACString &name)
295 {
296 if (!thread)
297 return;
299 thread->Dispatch(new nsNameThreadRunnable(name),
300 nsIEventTarget::DISPATCH_NORMAL);
301 }
303 #else // !XPCOM_GLUE_AVOID_NSPR
305 void
306 NS_SetThreadName(nsIThread *thread, const nsACString &name)
307 {
308 // No NSPR, no love.
309 }
311 #endif
313 #ifdef MOZILLA_INTERNAL_API
314 nsIThread *
315 NS_GetCurrentThread() {
316 return nsThreadManager::get()->GetCurrentThread();
317 }
318 #endif
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
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 }
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 }
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 }