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 "nsThreadManager.h"
8 #include "nsThread.h"
9 #include "nsThreadUtils.h"
10 #include "nsIClassInfoImpl.h"
11 #include "nsTArray.h"
12 #include "nsAutoPtr.h"
13 #ifdef MOZ_CANARY
14 #include <fcntl.h>
15 #include <unistd.h>
16 #endif
18 using namespace mozilla;
20 #ifdef XP_WIN
21 #include <windows.h>
22 DWORD gTLSThreadIDIndex = TlsAlloc();
23 #elif defined(NS_TLS)
24 NS_TLS mozilla::threads::ID gTLSThreadID = mozilla::threads::Generic;
25 #endif
27 typedef nsTArray< nsRefPtr<nsThread> > nsThreadArray;
29 //-----------------------------------------------------------------------------
31 static void
32 ReleaseObject(void *data)
33 {
34 static_cast<nsISupports *>(data)->Release();
35 }
37 static PLDHashOperator
38 AppendAndRemoveThread(PRThread *key, nsRefPtr<nsThread> &thread, void *arg)
39 {
40 nsThreadArray *threads = static_cast<nsThreadArray *>(arg);
41 threads->AppendElement(thread);
42 return PL_DHASH_REMOVE;
43 }
45 // statically allocated instance
46 NS_IMETHODIMP_(MozExternalRefCountType) nsThreadManager::AddRef() { return 2; }
47 NS_IMETHODIMP_(MozExternalRefCountType) nsThreadManager::Release() { return 1; }
48 NS_IMPL_CLASSINFO(nsThreadManager, nullptr,
49 nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON,
50 NS_THREADMANAGER_CID)
51 NS_IMPL_QUERY_INTERFACE_CI(nsThreadManager, nsIThreadManager)
52 NS_IMPL_CI_INTERFACE_GETTER(nsThreadManager, nsIThreadManager)
54 //-----------------------------------------------------------------------------
56 nsresult
57 nsThreadManager::Init()
58 {
59 // Child processes need to initialize the thread manager before they
60 // initialize XPCOM in order to set up the crash reporter. This leads to
61 // situations where we get initialized twice.
62 if (mInitialized)
63 return NS_OK;
65 if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE)
66 return NS_ERROR_FAILURE;
68 mLock = new Mutex("nsThreadManager.mLock");
70 #ifdef MOZ_CANARY
71 const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
72 const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
73 char* env_var_flag = getenv("MOZ_KILL_CANARIES");
74 sCanaryOutputFD = env_var_flag ? (env_var_flag[0] ?
75 open(env_var_flag, flags, mode) :
76 STDERR_FILENO) : 0;
77 #endif
79 // Setup "main" thread
80 mMainThread = new nsThread(nsThread::MAIN_THREAD, 0);
82 nsresult rv = mMainThread->InitCurrentThread();
83 if (NS_FAILED(rv)) {
84 mMainThread = nullptr;
85 return rv;
86 }
88 // We need to keep a pointer to the current thread, so we can satisfy
89 // GetIsMainThread calls that occur post-Shutdown.
90 mMainThread->GetPRThread(&mMainPRThread);
92 #ifdef XP_WIN
93 TlsSetValue(gTLSThreadIDIndex, (void*) mozilla::threads::Main);
94 #elif defined(NS_TLS)
95 gTLSThreadID = mozilla::threads::Main;
96 #endif
98 mInitialized = true;
99 return NS_OK;
100 }
102 void
103 nsThreadManager::Shutdown()
104 {
105 MOZ_ASSERT(NS_IsMainThread(), "shutdown not called from main thread");
107 // Prevent further access to the thread manager (no more new threads!)
108 //
109 // XXX What happens if shutdown happens before NewThread completes?
110 // Fortunately, NewThread is only called on the main thread for now.
111 //
112 mInitialized = false;
114 // Empty the main thread event queue before we begin shutting down threads.
115 NS_ProcessPendingEvents(mMainThread);
117 // We gather the threads from the hashtable into a list, so that we avoid
118 // holding the hashtable lock while calling nsIThread::Shutdown.
119 nsThreadArray threads;
120 {
121 MutexAutoLock lock(*mLock);
122 mThreadsByPRThread.Enumerate(AppendAndRemoveThread, &threads);
123 }
125 // It's tempting to walk the list of threads here and tell them each to stop
126 // accepting new events, but that could lead to badness if one of those
127 // threads is stuck waiting for a response from another thread. To do it
128 // right, we'd need some way to interrupt the threads.
129 //
130 // Instead, we process events on the current thread while waiting for threads
131 // to shutdown. This means that we have to preserve a mostly functioning
132 // world until such time as the threads exit.
134 // Shutdown all threads that require it (join with threads that we created).
135 for (uint32_t i = 0; i < threads.Length(); ++i) {
136 nsThread *thread = threads[i];
137 if (thread->ShutdownRequired())
138 thread->Shutdown();
139 }
141 // In case there are any more events somehow...
142 NS_ProcessPendingEvents(mMainThread);
144 // There are no more background threads at this point.
146 // Clear the table of threads.
147 {
148 MutexAutoLock lock(*mLock);
149 mThreadsByPRThread.Clear();
150 }
152 // Normally thread shutdown clears the observer for the thread, but since the
153 // main thread is special we do it manually here after we're sure all events
154 // have been processed.
155 mMainThread->SetObserver(nullptr);
156 mMainThread->ClearObservers();
158 // Release main thread object.
159 mMainThread = nullptr;
160 mLock = nullptr;
162 // Remove the TLS entry for the main thread.
163 PR_SetThreadPrivate(mCurThreadIndex, nullptr);
164 }
166 void
167 nsThreadManager::RegisterCurrentThread(nsThread *thread)
168 {
169 MOZ_ASSERT(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread");
171 MutexAutoLock lock(*mLock);
173 ++mCurrentNumberOfThreads;
174 if (mCurrentNumberOfThreads > mHighestNumberOfThreads) {
175 mHighestNumberOfThreads = mCurrentNumberOfThreads;
176 }
178 mThreadsByPRThread.Put(thread->GetPRThread(), thread); // XXX check OOM?
180 NS_ADDREF(thread); // for TLS entry
181 PR_SetThreadPrivate(mCurThreadIndex, thread);
182 }
184 void
185 nsThreadManager::UnregisterCurrentThread(nsThread *thread)
186 {
187 MOZ_ASSERT(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread");
189 MutexAutoLock lock(*mLock);
191 --mCurrentNumberOfThreads;
192 mThreadsByPRThread.Remove(thread->GetPRThread());
194 PR_SetThreadPrivate(mCurThreadIndex, nullptr);
195 // Ref-count balanced via ReleaseObject
196 }
198 nsThread *
199 nsThreadManager::GetCurrentThread()
200 {
201 // read thread local storage
202 void *data = PR_GetThreadPrivate(mCurThreadIndex);
203 if (data)
204 return static_cast<nsThread *>(data);
206 if (!mInitialized) {
207 return nullptr;
208 }
210 // OK, that's fine. We'll dynamically create one :-)
211 nsRefPtr<nsThread> thread = new nsThread(nsThread::NOT_MAIN_THREAD, 0);
212 if (!thread || NS_FAILED(thread->InitCurrentThread()))
213 return nullptr;
215 return thread.get(); // reference held in TLS
216 }
218 NS_IMETHODIMP
219 nsThreadManager::NewThread(uint32_t creationFlags,
220 uint32_t stackSize,
221 nsIThread **result)
222 {
223 // No new threads during Shutdown
224 if (NS_WARN_IF(!mInitialized))
225 return NS_ERROR_NOT_INITIALIZED;
227 nsThread *thr = new nsThread(nsThread::NOT_MAIN_THREAD, stackSize);
228 if (!thr)
229 return NS_ERROR_OUT_OF_MEMORY;
230 NS_ADDREF(thr);
232 nsresult rv = thr->Init();
233 if (NS_FAILED(rv)) {
234 NS_RELEASE(thr);
235 return rv;
236 }
238 // At this point, we expect that the thread has been registered in mThread;
239 // however, it is possible that it could have also been replaced by now, so
240 // we cannot really assert that it was added.
242 *result = thr;
243 return NS_OK;
244 }
246 NS_IMETHODIMP
247 nsThreadManager::GetThreadFromPRThread(PRThread *thread, nsIThread **result)
248 {
249 // Keep this functioning during Shutdown
250 if (NS_WARN_IF(!mMainThread))
251 return NS_ERROR_NOT_INITIALIZED;
252 if (NS_WARN_IF(!thread))
253 return NS_ERROR_INVALID_ARG;
255 nsRefPtr<nsThread> temp;
256 {
257 MutexAutoLock lock(*mLock);
258 mThreadsByPRThread.Get(thread, getter_AddRefs(temp));
259 }
261 NS_IF_ADDREF(*result = temp);
262 return NS_OK;
263 }
265 NS_IMETHODIMP
266 nsThreadManager::GetMainThread(nsIThread **result)
267 {
268 // Keep this functioning during Shutdown
269 if (NS_WARN_IF(!mMainThread))
270 return NS_ERROR_NOT_INITIALIZED;
271 NS_ADDREF(*result = mMainThread);
272 return NS_OK;
273 }
275 NS_IMETHODIMP
276 nsThreadManager::GetCurrentThread(nsIThread **result)
277 {
278 // Keep this functioning during Shutdown
279 if (NS_WARN_IF(!mMainThread))
280 return NS_ERROR_NOT_INITIALIZED;
281 *result = GetCurrentThread();
282 if (!*result)
283 return NS_ERROR_OUT_OF_MEMORY;
284 NS_ADDREF(*result);
285 return NS_OK;
286 }
288 NS_IMETHODIMP
289 nsThreadManager::GetIsMainThread(bool *result)
290 {
291 // This method may be called post-Shutdown
293 *result = (PR_GetCurrentThread() == mMainPRThread);
294 return NS_OK;
295 }
297 uint32_t
298 nsThreadManager::GetHighestNumberOfThreads()
299 {
300 MutexAutoLock lock(*mLock);
301 return mHighestNumberOfThreads;
302 }