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 "nsIClassInfoImpl.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 //-----------------------------------------------------------------------------
77 nsThreadManager::Init()
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();
92 return NS_ERROR_OUT_OF_MEMORY
;
94 nsresult rv
= mMainThread
->InitCurrentThread();
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
;
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())
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
);
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
);
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
196 nsThreadManager::GetCurrentThread()
198 // read thread local storage
199 void *data
= PR_GetThreadPrivate(mCurThreadIndex
);
201 return static_cast<nsThread
*>(data
);
207 // OK, that's fine. We'll dynamically create one :-)
208 nsRefPtr
<nsThread
> thread
= new nsThread();
209 if (!thread
|| NS_FAILED(thread
->InitCurrentThread()))
212 return thread
.get(); // reference held in TLS
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();
223 return NS_ERROR_OUT_OF_MEMORY
;
226 nsresult rv
= thr
->Init();
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.
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
);
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
);
267 nsThreadManager::GetCurrentThread(nsIThread
**result
)
269 // Keep this functioning during Shutdown
270 NS_ENSURE_TRUE(mMainThread
, NS_ERROR_NOT_INITIALIZED
);
271 *result
= GetCurrentThread();
273 return NS_ERROR_OUT_OF_MEMORY
;
279 nsThreadManager::GetIsMainThread(PRBool
*result
)
281 // This method may be called post-Shutdown
283 *result
= (PR_GetCurrentThread() == mMainPRThread
);