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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla code.
17 : *
18 : * The Initial Developer of the Original Code is Google Inc.
19 : * Portions created by the Initial Developer are Copyright (C) 2006
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@meer.net>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsThreadManager.h"
40 : #include "nsThread.h"
41 : #include "nsThreadUtils.h"
42 : #include "nsIClassInfoImpl.h"
43 : #include "nsTArray.h"
44 : #include "nsAutoPtr.h"
45 : #include "nsCycleCollectorUtils.h"
46 :
47 : using namespace mozilla;
48 :
49 : #ifdef XP_WIN
50 : #include <windows.h>
51 : DWORD gTLSThreadIDIndex = TlsAlloc();
52 : #elif defined(NS_TLS)
53 : NS_TLS mozilla::threads::ID gTLSThreadID = mozilla::threads::Generic;
54 : #endif
55 :
56 : typedef nsTArray< nsRefPtr<nsThread> > nsThreadArray;
57 :
58 : //-----------------------------------------------------------------------------
59 :
60 : static void
61 7525 : ReleaseObject(void *data)
62 : {
63 7525 : static_cast<nsISupports *>(data)->Release();
64 7525 : }
65 :
66 : static PLDHashOperator
67 3077 : AppendAndRemoveThread(const void *key, nsRefPtr<nsThread> &thread, void *arg)
68 : {
69 3077 : nsThreadArray *threads = static_cast<nsThreadArray *>(arg);
70 3077 : threads->AppendElement(thread);
71 3077 : return PL_DHASH_REMOVE;
72 : }
73 :
74 : //-----------------------------------------------------------------------------
75 :
76 1464 : nsThreadManager nsThreadManager::sInstance;
77 :
78 : // statically allocated instance
79 355023 : NS_IMETHODIMP_(nsrefcnt) nsThreadManager::AddRef() { return 2; }
80 355023 : NS_IMETHODIMP_(nsrefcnt) nsThreadManager::Release() { return 1; }
81 : NS_IMPL_CLASSINFO(nsThreadManager, NULL,
82 : nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON,
83 : NS_THREADMANAGER_CID)
84 207602 : NS_IMPL_QUERY_INTERFACE1_CI(nsThreadManager, nsIThreadManager)
85 1178 : NS_IMPL_CI_INTERFACE_GETTER1(nsThreadManager, nsIThreadManager)
86 :
87 : //-----------------------------------------------------------------------------
88 :
89 : nsresult
90 1419 : nsThreadManager::Init()
91 : {
92 1419 : if (!mThreadsByPRThread.Init())
93 0 : return NS_ERROR_OUT_OF_MEMORY;
94 :
95 1419 : if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE)
96 0 : return NS_ERROR_FAILURE;
97 :
98 1419 : mLock = new Mutex("nsThreadManager.mLock");
99 :
100 : // Setup "main" thread
101 1419 : mMainThread = new nsThread(nsThread::MAIN_THREAD, 0);
102 1419 : if (!mMainThread)
103 0 : return NS_ERROR_OUT_OF_MEMORY;
104 :
105 1419 : nsresult rv = mMainThread->InitCurrentThread();
106 1419 : if (NS_FAILED(rv)) {
107 0 : mMainThread = nsnull;
108 0 : return rv;
109 : }
110 :
111 : // We need to keep a pointer to the current thread, so we can satisfy
112 : // GetIsMainThread calls that occur post-Shutdown.
113 1419 : mMainThread->GetPRThread(&mMainPRThread);
114 :
115 : #ifdef XP_WIN
116 : TlsSetValue(gTLSThreadIDIndex, (void*) mozilla::threads::Main);
117 : #elif defined(NS_TLS)
118 1419 : gTLSThreadID = mozilla::threads::Main;
119 : #endif
120 :
121 1419 : mInitialized = true;
122 1419 : return NS_OK;
123 : }
124 :
125 : void
126 1419 : nsThreadManager::Shutdown()
127 : {
128 1419 : NS_ASSERTION(NS_IsMainThread(), "shutdown not called from main thread");
129 :
130 : // Prevent further access to the thread manager (no more new threads!)
131 : //
132 : // XXX What happens if shutdown happens before NewThread completes?
133 : // Fortunately, NewThread is only called on the main thread for now.
134 : //
135 1419 : mInitialized = false;
136 :
137 : // Empty the main thread event queue before we begin shutting down threads.
138 1419 : NS_ProcessPendingEvents(mMainThread);
139 :
140 : // We gather the threads from the hashtable into a list, so that we avoid
141 : // holding the hashtable lock while calling nsIThread::Shutdown.
142 2838 : nsThreadArray threads;
143 : {
144 2838 : MutexAutoLock lock(*mLock);
145 1419 : mThreadsByPRThread.Enumerate(AppendAndRemoveThread, &threads);
146 : }
147 :
148 : // It's tempting to walk the list of threads here and tell them each to stop
149 : // accepting new events, but that could lead to badness if one of those
150 : // threads is stuck waiting for a response from another thread. To do it
151 : // right, we'd need some way to interrupt the threads.
152 : //
153 : // Instead, we process events on the current thread while waiting for threads
154 : // to shutdown. This means that we have to preserve a mostly functioning
155 : // world until such time as the threads exit.
156 :
157 : // Shutdown all threads that require it (join with threads that we created).
158 4496 : for (PRUint32 i = 0; i < threads.Length(); ++i) {
159 3077 : nsThread *thread = threads[i];
160 3077 : if (thread->ShutdownRequired())
161 1658 : thread->Shutdown();
162 : }
163 :
164 : // In case there are any more events somehow...
165 1419 : NS_ProcessPendingEvents(mMainThread);
166 :
167 : // There are no more background threads at this point.
168 :
169 : // Clear the table of threads.
170 : {
171 2838 : MutexAutoLock lock(*mLock);
172 1419 : mThreadsByPRThread.Clear();
173 : }
174 :
175 : // Normally thread shutdown clears the observer for the thread, but since the
176 : // main thread is special we do it manually here after we're sure all events
177 : // have been processed.
178 1419 : mMainThread->SetObserver(nsnull);
179 1419 : mMainThread->ClearObservers();
180 :
181 : // Release main thread object.
182 1419 : mMainThread = nsnull;
183 1419 : mLock = nsnull;
184 :
185 : // Remove the TLS entry for the main thread.
186 1419 : PR_SetThreadPrivate(mCurThreadIndex, nsnull);
187 1419 : }
188 :
189 : void
190 7525 : nsThreadManager::RegisterCurrentThread(nsThread *thread)
191 : {
192 7525 : NS_ASSERTION(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread");
193 :
194 15050 : MutexAutoLock lock(*mLock);
195 :
196 7525 : mThreadsByPRThread.Put(thread->GetPRThread(), thread); // XXX check OOM?
197 :
198 7525 : NS_ADDREF(thread); // for TLS entry
199 7525 : PR_SetThreadPrivate(mCurThreadIndex, thread);
200 7525 : }
201 :
202 : void
203 6106 : nsThreadManager::UnregisterCurrentThread(nsThread *thread)
204 : {
205 6106 : NS_ASSERTION(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread");
206 :
207 12212 : MutexAutoLock lock(*mLock);
208 :
209 6106 : mThreadsByPRThread.Remove(thread->GetPRThread());
210 :
211 6106 : PR_SetThreadPrivate(mCurThreadIndex, nsnull);
212 : // Ref-count balanced via ReleaseObject
213 6106 : }
214 :
215 : nsThread *
216 106687 : nsThreadManager::GetCurrentThread()
217 : {
218 : // read thread local storage
219 106687 : void *data = PR_GetThreadPrivate(mCurThreadIndex);
220 106688 : if (data)
221 105039 : return static_cast<nsThread *>(data);
222 :
223 1649 : if (!mInitialized) {
224 1649 : return nsnull;
225 : }
226 :
227 : // OK, that's fine. We'll dynamically create one :-)
228 0 : nsRefPtr<nsThread> thread = new nsThread(nsThread::NOT_MAIN_THREAD, 0);
229 0 : if (!thread || NS_FAILED(thread->InitCurrentThread()))
230 0 : return nsnull;
231 :
232 0 : return thread.get(); // reference held in TLS
233 : }
234 :
235 : NS_IMETHODIMP
236 6106 : nsThreadManager::NewThread(PRUint32 creationFlags,
237 : PRUint32 stackSize,
238 : nsIThread **result)
239 : {
240 : // No new threads during Shutdown
241 6106 : NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
242 :
243 6106 : nsThread *thr = new nsThread(nsThread::NOT_MAIN_THREAD, stackSize);
244 6106 : if (!thr)
245 0 : return NS_ERROR_OUT_OF_MEMORY;
246 6106 : NS_ADDREF(thr);
247 :
248 6106 : nsresult rv = thr->Init();
249 6106 : if (NS_FAILED(rv)) {
250 0 : NS_RELEASE(thr);
251 0 : return rv;
252 : }
253 :
254 : // At this point, we expect that the thread has been registered in mThread;
255 : // however, it is possible that it could have also been replaced by now, so
256 : // we cannot really assert that it was added.
257 :
258 6106 : *result = thr;
259 6106 : return NS_OK;
260 : }
261 :
262 : NS_IMETHODIMP
263 1 : nsThreadManager::GetThreadFromPRThread(PRThread *thread, nsIThread **result)
264 : {
265 : // Keep this functioning during Shutdown
266 1 : NS_ENSURE_TRUE(mMainThread, NS_ERROR_NOT_INITIALIZED);
267 1 : NS_ENSURE_ARG_POINTER(thread);
268 :
269 2 : nsRefPtr<nsThread> temp;
270 : {
271 2 : MutexAutoLock lock(*mLock);
272 1 : mThreadsByPRThread.Get(thread, getter_AddRefs(temp));
273 : }
274 :
275 1 : NS_IF_ADDREF(*result = temp);
276 1 : return NS_OK;
277 : }
278 :
279 : NS_IMETHODIMP
280 71324 : nsThreadManager::GetMainThread(nsIThread **result)
281 : {
282 : // Keep this functioning during Shutdown
283 71324 : NS_ENSURE_TRUE(mMainThread, NS_ERROR_NOT_INITIALIZED);
284 71307 : NS_ADDREF(*result = mMainThread);
285 71307 : return NS_OK;
286 : }
287 :
288 : NS_IMETHODIMP
289 72958 : nsThreadManager::GetCurrentThread(nsIThread **result)
290 : {
291 : // Keep this functioning during Shutdown
292 72958 : NS_ENSURE_TRUE(mMainThread, NS_ERROR_NOT_INITIALIZED);
293 72941 : *result = GetCurrentThread();
294 72941 : if (!*result)
295 0 : return NS_ERROR_OUT_OF_MEMORY;
296 72941 : NS_ADDREF(*result);
297 72941 : return NS_OK;
298 : }
299 :
300 : NS_IMETHODIMP
301 54 : nsThreadManager::GetIsMainThread(bool *result)
302 : {
303 : // This method may be called post-Shutdown
304 :
305 54 : *result = (PR_GetCurrentThread() == mMainPRThread);
306 54 : return NS_OK;
307 : }
308 :
309 : NS_IMETHODIMP
310 158361 : nsThreadManager::GetIsCycleCollectorThread(bool *result)
311 : {
312 158361 : *result = bool(NS_IsCycleCollectorThread());
313 158361 : return NS_OK;
314 4392 : }
|