On x86 compilers without fastcall, simulate it when invoking traces and un-simulate...
[wine-gecko.git] / xpcom / threads / nsThreadManager.cpp
blobd06171ea488aa27ee086142c8ac38104ad30dd50
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 "nsIClassInfoImpl.h"
42 #include "nsTArray.h"
43 #include "nsAutoPtr.h"
44 #include "nsAutoLock.h"
46 typedef nsTArray< nsRefPtr<nsThread> > nsThreadArray;
48 //-----------------------------------------------------------------------------
50 PR_STATIC_CALLBACK(void)
51 ReleaseObject(void *data)
53 static_cast<nsISupports *>(data)->Release();
56 PR_STATIC_CALLBACK(PLDHashOperator)
57 AppendAndRemoveThread(const void *key, nsRefPtr<nsThread> &thread, void *arg)
59 nsThreadArray *threads = static_cast<nsThreadArray *>(arg);
60 threads->AppendElement(thread);
61 return PL_DHASH_REMOVE;
64 //-----------------------------------------------------------------------------
66 nsThreadManager nsThreadManager::sInstance;
68 // statically allocated instance
69 NS_IMETHODIMP_(nsrefcnt) nsThreadManager::AddRef() { return 2; }
70 NS_IMETHODIMP_(nsrefcnt) nsThreadManager::Release() { return 1; }
71 NS_IMPL_QUERY_INTERFACE1_CI(nsThreadManager, nsIThreadManager)
72 NS_IMPL_CI_INTERFACE_GETTER1(nsThreadManager, nsIThreadManager)
74 //-----------------------------------------------------------------------------
76 nsresult
77 nsThreadManager::Init()
79 mLock = PR_NewLock();
80 if (!mLock)
81 return NS_ERROR_OUT_OF_MEMORY;
83 if (!mThreadsByPRThread.Init())
84 return NS_ERROR_OUT_OF_MEMORY;
86 if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE)
87 return NS_ERROR_FAILURE;
89 // Setup "main" thread
90 mMainThread = new nsThread();
91 if (!mMainThread)
92 return NS_ERROR_OUT_OF_MEMORY;
94 nsresult rv = mMainThread->InitCurrentThread();
95 if (NS_FAILED(rv)) {
96 mMainThread = nsnull;
97 return rv;
100 // We need to keep a pointer to the current thread, so we can satisfy
101 // GetIsMainThread calls that occur post-Shutdown.
102 mMainThread->GetPRThread(&mMainPRThread);
104 mInitialized = PR_TRUE;
105 return NS_OK;
108 void
109 nsThreadManager::Shutdown()
111 NS_ASSERTION(NS_IsMainThread(), "shutdown not called from main thread");
113 // Prevent further access to the thread manager (no more new threads!)
115 // XXX What happens if shutdown happens before NewThread completes?
116 // Fortunately, NewThread is only called on the main thread for now.
118 mInitialized = PR_FALSE;
120 // Empty the main thread event queue before we begin shutting down threads.
121 NS_ProcessPendingEvents(mMainThread);
123 // We gather the threads from the hashtable into a list, so that we avoid
124 // holding the hashtable lock while calling nsIThread::Shutdown.
125 nsThreadArray threads;
127 nsAutoLock lock(mLock);
128 mThreadsByPRThread.Enumerate(AppendAndRemoveThread, &threads);
131 // It's tempting to walk the list of threads here and tell them each to stop
132 // accepting new events, but that could lead to badness if one of those
133 // threads is stuck waiting for a response from another thread. To do it
134 // right, we'd need some way to interrupt the threads.
136 // Instead, we process events on the current thread while waiting for threads
137 // to shutdown. This means that we have to preserve a mostly functioning
138 // world until such time as the threads exit.
140 // Shutdown all threads that require it (join with threads that we created).
141 for (PRUint32 i = 0; i < threads.Length(); ++i) {
142 nsThread *thread = threads[i];
143 if (thread->ShutdownRequired())
144 thread->Shutdown();
147 // In case there are any more events somehow...
148 NS_ProcessPendingEvents(mMainThread);
150 // There are no more background threads at this point.
152 // Clear the table of threads.
154 nsAutoLock lock(mLock);
155 mThreadsByPRThread.Clear();
158 // Release main thread object.
159 mMainThread = nsnull;
161 // Remove the TLS entry for the main thread.
162 PR_SetThreadPrivate(mCurThreadIndex, nsnull);
164 // We don't need this lock anymore.
165 PR_DestroyLock(mLock);
166 mLock = nsnull;
169 void
170 nsThreadManager::RegisterCurrentThread(nsThread *thread)
172 NS_ASSERTION(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread");
174 nsAutoLock lock(mLock);
176 mThreadsByPRThread.Put(thread->GetPRThread(), thread); // XXX check OOM?
178 NS_ADDREF(thread); // for TLS entry
179 PR_SetThreadPrivate(mCurThreadIndex, thread);
182 void
183 nsThreadManager::UnregisterCurrentThread(nsThread *thread)
185 NS_ASSERTION(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread");
187 nsAutoLock lock(mLock);
189 mThreadsByPRThread.Remove(thread->GetPRThread());
191 PR_SetThreadPrivate(mCurThreadIndex, nsnull);
192 // Ref-count balanced via ReleaseObject
195 nsThread *
196 nsThreadManager::GetCurrentThread()
198 // read thread local storage
199 void *data = PR_GetThreadPrivate(mCurThreadIndex);
200 if (data)
201 return static_cast<nsThread *>(data);
203 if (!mInitialized) {
204 return nsnull;
207 // OK, that's fine. We'll dynamically create one :-)
208 nsRefPtr<nsThread> thread = new nsThread();
209 if (!thread || NS_FAILED(thread->InitCurrentThread()))
210 return nsnull;
212 return thread.get(); // reference held in TLS
215 NS_IMETHODIMP
216 nsThreadManager::NewThread(PRUint32 creationFlags, nsIThread **result)
218 // No new threads during Shutdown
219 NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
221 nsThread *thr = new nsThread();
222 if (!thr)
223 return NS_ERROR_OUT_OF_MEMORY;
224 NS_ADDREF(thr);
226 nsresult rv = thr->Init();
227 if (NS_FAILED(rv)) {
228 NS_RELEASE(thr);
229 return rv;
232 // At this point, we expect that the thread has been registered in mThread;
233 // however, it is possible that it could have also been replaced by now, so
234 // we cannot really assert that it was added.
236 *result = thr;
237 return NS_OK;
240 NS_IMETHODIMP
241 nsThreadManager::GetThreadFromPRThread(PRThread *thread, nsIThread **result)
243 // Keep this functioning during Shutdown
244 NS_ENSURE_TRUE(mMainThread, NS_ERROR_NOT_INITIALIZED);
245 NS_ENSURE_ARG_POINTER(thread);
247 nsRefPtr<nsThread> temp;
249 nsAutoLock lock(mLock);
250 mThreadsByPRThread.Get(thread, getter_AddRefs(temp));
253 NS_IF_ADDREF(*result = temp);
254 return NS_OK;
257 NS_IMETHODIMP
258 nsThreadManager::GetMainThread(nsIThread **result)
260 // Keep this functioning during Shutdown
261 NS_ENSURE_TRUE(mMainThread, NS_ERROR_NOT_INITIALIZED);
262 NS_ADDREF(*result = mMainThread);
263 return NS_OK;
266 NS_IMETHODIMP
267 nsThreadManager::GetCurrentThread(nsIThread **result)
269 // Keep this functioning during Shutdown
270 NS_ENSURE_TRUE(mMainThread, NS_ERROR_NOT_INITIALIZED);
271 *result = GetCurrentThread();
272 if (!*result)
273 return NS_ERROR_OUT_OF_MEMORY;
274 NS_ADDREF(*result);
275 return NS_OK;
278 NS_IMETHODIMP
279 nsThreadManager::GetIsMainThread(PRBool *result)
281 // This method may be called post-Shutdown
283 *result = (PR_GetCurrentThread() == mMainPRThread);
284 return NS_OK;