Bug 575870 - Enable the firefox button on xp themed, classic, and aero basic. r=dao...
[mozilla-central.git] / xpcom / threads / nsThreadManager.cpp
blob0416ef9221c970c5de429d8136e93da74f67be39
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
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/
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.
16 * The Original Code is Mozilla code.
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.
22 * Contributor(s):
23 * Darin Fisher <darin@meer.net>
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.
37 * ***** END LICENSE BLOCK ***** */
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 "nsAutoLock.h"
47 #ifdef XP_WIN
48 #include <windows.h>
49 DWORD gTLSIsMainThreadIndex = TlsAlloc();
50 #elif defined(NS_TLS)
51 NS_TLS bool gTLSIsMainThread = false;
52 #endif
54 typedef nsTArray< nsRefPtr<nsThread> > nsThreadArray;
56 //-----------------------------------------------------------------------------
58 static void
59 ReleaseObject(void *data)
61 static_cast<nsISupports *>(data)->Release();
64 static PLDHashOperator
65 AppendAndRemoveThread(const void *key, nsRefPtr<nsThread> &thread, void *arg)
67 nsThreadArray *threads = static_cast<nsThreadArray *>(arg);
68 threads->AppendElement(thread);
69 return PL_DHASH_REMOVE;
72 //-----------------------------------------------------------------------------
74 nsThreadManager nsThreadManager::sInstance;
76 // statically allocated instance
77 NS_IMETHODIMP_(nsrefcnt) nsThreadManager::AddRef() { return 2; }
78 NS_IMETHODIMP_(nsrefcnt) nsThreadManager::Release() { return 1; }
79 NS_IMPL_CLASSINFO(nsThreadManager, NULL,
80 nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON,
81 NS_THREADMANAGER_CID)
82 NS_IMPL_QUERY_INTERFACE1_CI(nsThreadManager, nsIThreadManager)
83 NS_IMPL_CI_INTERFACE_GETTER1(nsThreadManager, nsIThreadManager)
85 //-----------------------------------------------------------------------------
87 nsresult
88 nsThreadManager::Init()
90 mLock = PR_NewLock();
91 if (!mLock)
92 return NS_ERROR_OUT_OF_MEMORY;
94 if (!mThreadsByPRThread.Init())
95 return NS_ERROR_OUT_OF_MEMORY;
97 if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE)
98 return NS_ERROR_FAILURE;
100 // Setup "main" thread
101 mMainThread = new nsThread();
102 if (!mMainThread)
103 return NS_ERROR_OUT_OF_MEMORY;
105 nsresult rv = mMainThread->InitCurrentThread();
106 if (NS_FAILED(rv)) {
107 mMainThread = nsnull;
108 return rv;
111 // We need to keep a pointer to the current thread, so we can satisfy
112 // GetIsMainThread calls that occur post-Shutdown.
113 mMainThread->GetPRThread(&mMainPRThread);
115 #ifdef XP_WIN
116 TlsSetValue(gTLSIsMainThreadIndex, (void*) 1);
117 #elif defined(NS_TLS)
118 gTLSIsMainThread = true;
119 #endif
121 mInitialized = PR_TRUE;
122 return NS_OK;
125 void
126 nsThreadManager::Shutdown()
128 NS_ASSERTION(NS_IsMainThread(), "shutdown not called from main thread");
130 // Prevent further access to the thread manager (no more new threads!)
132 // XXX What happens if shutdown happens before NewThread completes?
133 // Fortunately, NewThread is only called on the main thread for now.
135 mInitialized = PR_FALSE;
137 // Empty the main thread event queue before we begin shutting down threads.
138 NS_ProcessPendingEvents(mMainThread);
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 nsThreadArray threads;
144 nsAutoLock lock(mLock);
145 mThreadsByPRThread.Enumerate(AppendAndRemoveThread, &threads);
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.
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.
157 // Shutdown all threads that require it (join with threads that we created).
158 for (PRUint32 i = 0; i < threads.Length(); ++i) {
159 nsThread *thread = threads[i];
160 if (thread->ShutdownRequired())
161 thread->Shutdown();
164 // In case there are any more events somehow...
165 NS_ProcessPendingEvents(mMainThread);
167 // There are no more background threads at this point.
169 // Clear the table of threads.
171 nsAutoLock lock(mLock);
172 mThreadsByPRThread.Clear();
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 mMainThread->SetObserver(nsnull);
180 // Release main thread object.
181 mMainThread = nsnull;
183 // Remove the TLS entry for the main thread.
184 PR_SetThreadPrivate(mCurThreadIndex, nsnull);
186 // We don't need this lock anymore.
187 PR_DestroyLock(mLock);
188 mLock = nsnull;
191 void
192 nsThreadManager::RegisterCurrentThread(nsThread *thread)
194 NS_ASSERTION(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread");
196 nsAutoLock lock(mLock);
198 mThreadsByPRThread.Put(thread->GetPRThread(), thread); // XXX check OOM?
200 NS_ADDREF(thread); // for TLS entry
201 PR_SetThreadPrivate(mCurThreadIndex, thread);
204 void
205 nsThreadManager::UnregisterCurrentThread(nsThread *thread)
207 NS_ASSERTION(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread");
209 nsAutoLock lock(mLock);
211 mThreadsByPRThread.Remove(thread->GetPRThread());
213 PR_SetThreadPrivate(mCurThreadIndex, nsnull);
214 // Ref-count balanced via ReleaseObject
217 nsThread *
218 nsThreadManager::GetCurrentThread()
220 // read thread local storage
221 void *data = PR_GetThreadPrivate(mCurThreadIndex);
222 if (data)
223 return static_cast<nsThread *>(data);
225 if (!mInitialized) {
226 return nsnull;
229 // OK, that's fine. We'll dynamically create one :-)
230 nsRefPtr<nsThread> thread = new nsThread();
231 if (!thread || NS_FAILED(thread->InitCurrentThread()))
232 return nsnull;
234 return thread.get(); // reference held in TLS
237 NS_IMETHODIMP
238 nsThreadManager::NewThread(PRUint32 creationFlags, nsIThread **result)
240 // No new threads during Shutdown
241 NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
243 nsThread *thr = new nsThread();
244 if (!thr)
245 return NS_ERROR_OUT_OF_MEMORY;
246 NS_ADDREF(thr);
248 nsresult rv = thr->Init();
249 if (NS_FAILED(rv)) {
250 NS_RELEASE(thr);
251 return rv;
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.
258 *result = thr;
259 return NS_OK;
262 NS_IMETHODIMP
263 nsThreadManager::GetThreadFromPRThread(PRThread *thread, nsIThread **result)
265 // Keep this functioning during Shutdown
266 NS_ENSURE_TRUE(mMainThread, NS_ERROR_NOT_INITIALIZED);
267 NS_ENSURE_ARG_POINTER(thread);
269 nsRefPtr<nsThread> temp;
271 nsAutoLock lock(mLock);
272 mThreadsByPRThread.Get(thread, getter_AddRefs(temp));
275 NS_IF_ADDREF(*result = temp);
276 return NS_OK;
279 NS_IMETHODIMP
280 nsThreadManager::GetMainThread(nsIThread **result)
282 // Keep this functioning during Shutdown
283 NS_ENSURE_TRUE(mMainThread, NS_ERROR_NOT_INITIALIZED);
284 NS_ADDREF(*result = mMainThread);
285 return NS_OK;
288 NS_IMETHODIMP
289 nsThreadManager::GetCurrentThread(nsIThread **result)
291 // Keep this functioning during Shutdown
292 NS_ENSURE_TRUE(mMainThread, NS_ERROR_NOT_INITIALIZED);
293 *result = GetCurrentThread();
294 if (!*result)
295 return NS_ERROR_OUT_OF_MEMORY;
296 NS_ADDREF(*result);
297 return NS_OK;
300 NS_IMETHODIMP
301 nsThreadManager::GetIsMainThread(PRBool *result)
303 // This method may be called post-Shutdown
305 *result = (PR_GetCurrentThread() == mMainPRThread);
306 return NS_OK;