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
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.
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"
41 #include "nsThreadUtils.h"
42 #include "nsIClassInfoImpl.h"
44 #include "nsAutoPtr.h"
45 #include "nsAutoLock.h"
49 DWORD gTLSIsMainThreadIndex
= TlsAlloc();
51 NS_TLS
bool gTLSIsMainThread
= false;
54 typedef nsTArray
< nsRefPtr
<nsThread
> > nsThreadArray
;
56 //-----------------------------------------------------------------------------
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
,
82 NS_IMPL_QUERY_INTERFACE1_CI(nsThreadManager
, nsIThreadManager
)
83 NS_IMPL_CI_INTERFACE_GETTER1(nsThreadManager
, nsIThreadManager
)
85 //-----------------------------------------------------------------------------
88 nsThreadManager::Init()
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();
103 return NS_ERROR_OUT_OF_MEMORY
;
105 nsresult rv
= mMainThread
->InitCurrentThread();
107 mMainThread
= nsnull
;
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
);
116 TlsSetValue(gTLSIsMainThreadIndex
, (void*) 1);
117 #elif defined(NS_TLS)
118 gTLSIsMainThread
= true;
121 mInitialized
= PR_TRUE
;
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())
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
);
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
);
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
218 nsThreadManager::GetCurrentThread()
220 // read thread local storage
221 void *data
= PR_GetThreadPrivate(mCurThreadIndex
);
223 return static_cast<nsThread
*>(data
);
229 // OK, that's fine. We'll dynamically create one :-)
230 nsRefPtr
<nsThread
> thread
= new nsThread();
231 if (!thread
|| NS_FAILED(thread
->InitCurrentThread()))
234 return thread
.get(); // reference held in TLS
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();
245 return NS_ERROR_OUT_OF_MEMORY
;
248 nsresult rv
= thr
->Init();
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.
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
);
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
);
289 nsThreadManager::GetCurrentThread(nsIThread
**result
)
291 // Keep this functioning during Shutdown
292 NS_ENSURE_TRUE(mMainThread
, NS_ERROR_NOT_INITIALIZED
);
293 *result
= GetCurrentThread();
295 return NS_ERROR_OUT_OF_MEMORY
;
301 nsThreadManager::GetIsMainThread(PRBool
*result
)
303 // This method may be called post-Shutdown
305 *result
= (PR_GetCurrentThread() == mMainPRThread
);