1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/threads/nsThreadManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,302 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsThreadManager.h" 1.11 +#include "nsThread.h" 1.12 +#include "nsThreadUtils.h" 1.13 +#include "nsIClassInfoImpl.h" 1.14 +#include "nsTArray.h" 1.15 +#include "nsAutoPtr.h" 1.16 +#ifdef MOZ_CANARY 1.17 +#include <fcntl.h> 1.18 +#include <unistd.h> 1.19 +#endif 1.20 + 1.21 +using namespace mozilla; 1.22 + 1.23 +#ifdef XP_WIN 1.24 +#include <windows.h> 1.25 +DWORD gTLSThreadIDIndex = TlsAlloc(); 1.26 +#elif defined(NS_TLS) 1.27 +NS_TLS mozilla::threads::ID gTLSThreadID = mozilla::threads::Generic; 1.28 +#endif 1.29 + 1.30 +typedef nsTArray< nsRefPtr<nsThread> > nsThreadArray; 1.31 + 1.32 +//----------------------------------------------------------------------------- 1.33 + 1.34 +static void 1.35 +ReleaseObject(void *data) 1.36 +{ 1.37 + static_cast<nsISupports *>(data)->Release(); 1.38 +} 1.39 + 1.40 +static PLDHashOperator 1.41 +AppendAndRemoveThread(PRThread *key, nsRefPtr<nsThread> &thread, void *arg) 1.42 +{ 1.43 + nsThreadArray *threads = static_cast<nsThreadArray *>(arg); 1.44 + threads->AppendElement(thread); 1.45 + return PL_DHASH_REMOVE; 1.46 +} 1.47 + 1.48 +// statically allocated instance 1.49 +NS_IMETHODIMP_(MozExternalRefCountType) nsThreadManager::AddRef() { return 2; } 1.50 +NS_IMETHODIMP_(MozExternalRefCountType) nsThreadManager::Release() { return 1; } 1.51 +NS_IMPL_CLASSINFO(nsThreadManager, nullptr, 1.52 + nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON, 1.53 + NS_THREADMANAGER_CID) 1.54 +NS_IMPL_QUERY_INTERFACE_CI(nsThreadManager, nsIThreadManager) 1.55 +NS_IMPL_CI_INTERFACE_GETTER(nsThreadManager, nsIThreadManager) 1.56 + 1.57 +//----------------------------------------------------------------------------- 1.58 + 1.59 +nsresult 1.60 +nsThreadManager::Init() 1.61 +{ 1.62 + // Child processes need to initialize the thread manager before they 1.63 + // initialize XPCOM in order to set up the crash reporter. This leads to 1.64 + // situations where we get initialized twice. 1.65 + if (mInitialized) 1.66 + return NS_OK; 1.67 + 1.68 + if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE) 1.69 + return NS_ERROR_FAILURE; 1.70 + 1.71 + mLock = new Mutex("nsThreadManager.mLock"); 1.72 + 1.73 +#ifdef MOZ_CANARY 1.74 + const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK; 1.75 + const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; 1.76 + char* env_var_flag = getenv("MOZ_KILL_CANARIES"); 1.77 + sCanaryOutputFD = env_var_flag ? (env_var_flag[0] ? 1.78 + open(env_var_flag, flags, mode) : 1.79 + STDERR_FILENO) : 0; 1.80 +#endif 1.81 + 1.82 + // Setup "main" thread 1.83 + mMainThread = new nsThread(nsThread::MAIN_THREAD, 0); 1.84 + 1.85 + nsresult rv = mMainThread->InitCurrentThread(); 1.86 + if (NS_FAILED(rv)) { 1.87 + mMainThread = nullptr; 1.88 + return rv; 1.89 + } 1.90 + 1.91 + // We need to keep a pointer to the current thread, so we can satisfy 1.92 + // GetIsMainThread calls that occur post-Shutdown. 1.93 + mMainThread->GetPRThread(&mMainPRThread); 1.94 + 1.95 +#ifdef XP_WIN 1.96 + TlsSetValue(gTLSThreadIDIndex, (void*) mozilla::threads::Main); 1.97 +#elif defined(NS_TLS) 1.98 + gTLSThreadID = mozilla::threads::Main; 1.99 +#endif 1.100 + 1.101 + mInitialized = true; 1.102 + return NS_OK; 1.103 +} 1.104 + 1.105 +void 1.106 +nsThreadManager::Shutdown() 1.107 +{ 1.108 + MOZ_ASSERT(NS_IsMainThread(), "shutdown not called from main thread"); 1.109 + 1.110 + // Prevent further access to the thread manager (no more new threads!) 1.111 + // 1.112 + // XXX What happens if shutdown happens before NewThread completes? 1.113 + // Fortunately, NewThread is only called on the main thread for now. 1.114 + // 1.115 + mInitialized = false; 1.116 + 1.117 + // Empty the main thread event queue before we begin shutting down threads. 1.118 + NS_ProcessPendingEvents(mMainThread); 1.119 + 1.120 + // We gather the threads from the hashtable into a list, so that we avoid 1.121 + // holding the hashtable lock while calling nsIThread::Shutdown. 1.122 + nsThreadArray threads; 1.123 + { 1.124 + MutexAutoLock lock(*mLock); 1.125 + mThreadsByPRThread.Enumerate(AppendAndRemoveThread, &threads); 1.126 + } 1.127 + 1.128 + // It's tempting to walk the list of threads here and tell them each to stop 1.129 + // accepting new events, but that could lead to badness if one of those 1.130 + // threads is stuck waiting for a response from another thread. To do it 1.131 + // right, we'd need some way to interrupt the threads. 1.132 + // 1.133 + // Instead, we process events on the current thread while waiting for threads 1.134 + // to shutdown. This means that we have to preserve a mostly functioning 1.135 + // world until such time as the threads exit. 1.136 + 1.137 + // Shutdown all threads that require it (join with threads that we created). 1.138 + for (uint32_t i = 0; i < threads.Length(); ++i) { 1.139 + nsThread *thread = threads[i]; 1.140 + if (thread->ShutdownRequired()) 1.141 + thread->Shutdown(); 1.142 + } 1.143 + 1.144 + // In case there are any more events somehow... 1.145 + NS_ProcessPendingEvents(mMainThread); 1.146 + 1.147 + // There are no more background threads at this point. 1.148 + 1.149 + // Clear the table of threads. 1.150 + { 1.151 + MutexAutoLock lock(*mLock); 1.152 + mThreadsByPRThread.Clear(); 1.153 + } 1.154 + 1.155 + // Normally thread shutdown clears the observer for the thread, but since the 1.156 + // main thread is special we do it manually here after we're sure all events 1.157 + // have been processed. 1.158 + mMainThread->SetObserver(nullptr); 1.159 + mMainThread->ClearObservers(); 1.160 + 1.161 + // Release main thread object. 1.162 + mMainThread = nullptr; 1.163 + mLock = nullptr; 1.164 + 1.165 + // Remove the TLS entry for the main thread. 1.166 + PR_SetThreadPrivate(mCurThreadIndex, nullptr); 1.167 +} 1.168 + 1.169 +void 1.170 +nsThreadManager::RegisterCurrentThread(nsThread *thread) 1.171 +{ 1.172 + MOZ_ASSERT(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread"); 1.173 + 1.174 + MutexAutoLock lock(*mLock); 1.175 + 1.176 + ++mCurrentNumberOfThreads; 1.177 + if (mCurrentNumberOfThreads > mHighestNumberOfThreads) { 1.178 + mHighestNumberOfThreads = mCurrentNumberOfThreads; 1.179 + } 1.180 + 1.181 + mThreadsByPRThread.Put(thread->GetPRThread(), thread); // XXX check OOM? 1.182 + 1.183 + NS_ADDREF(thread); // for TLS entry 1.184 + PR_SetThreadPrivate(mCurThreadIndex, thread); 1.185 +} 1.186 + 1.187 +void 1.188 +nsThreadManager::UnregisterCurrentThread(nsThread *thread) 1.189 +{ 1.190 + MOZ_ASSERT(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread"); 1.191 + 1.192 + MutexAutoLock lock(*mLock); 1.193 + 1.194 + --mCurrentNumberOfThreads; 1.195 + mThreadsByPRThread.Remove(thread->GetPRThread()); 1.196 + 1.197 + PR_SetThreadPrivate(mCurThreadIndex, nullptr); 1.198 + // Ref-count balanced via ReleaseObject 1.199 +} 1.200 + 1.201 +nsThread * 1.202 +nsThreadManager::GetCurrentThread() 1.203 +{ 1.204 + // read thread local storage 1.205 + void *data = PR_GetThreadPrivate(mCurThreadIndex); 1.206 + if (data) 1.207 + return static_cast<nsThread *>(data); 1.208 + 1.209 + if (!mInitialized) { 1.210 + return nullptr; 1.211 + } 1.212 + 1.213 + // OK, that's fine. We'll dynamically create one :-) 1.214 + nsRefPtr<nsThread> thread = new nsThread(nsThread::NOT_MAIN_THREAD, 0); 1.215 + if (!thread || NS_FAILED(thread->InitCurrentThread())) 1.216 + return nullptr; 1.217 + 1.218 + return thread.get(); // reference held in TLS 1.219 +} 1.220 + 1.221 +NS_IMETHODIMP 1.222 +nsThreadManager::NewThread(uint32_t creationFlags, 1.223 + uint32_t stackSize, 1.224 + nsIThread **result) 1.225 +{ 1.226 + // No new threads during Shutdown 1.227 + if (NS_WARN_IF(!mInitialized)) 1.228 + return NS_ERROR_NOT_INITIALIZED; 1.229 + 1.230 + nsThread *thr = new nsThread(nsThread::NOT_MAIN_THREAD, stackSize); 1.231 + if (!thr) 1.232 + return NS_ERROR_OUT_OF_MEMORY; 1.233 + NS_ADDREF(thr); 1.234 + 1.235 + nsresult rv = thr->Init(); 1.236 + if (NS_FAILED(rv)) { 1.237 + NS_RELEASE(thr); 1.238 + return rv; 1.239 + } 1.240 + 1.241 + // At this point, we expect that the thread has been registered in mThread; 1.242 + // however, it is possible that it could have also been replaced by now, so 1.243 + // we cannot really assert that it was added. 1.244 + 1.245 + *result = thr; 1.246 + return NS_OK; 1.247 +} 1.248 + 1.249 +NS_IMETHODIMP 1.250 +nsThreadManager::GetThreadFromPRThread(PRThread *thread, nsIThread **result) 1.251 +{ 1.252 + // Keep this functioning during Shutdown 1.253 + if (NS_WARN_IF(!mMainThread)) 1.254 + return NS_ERROR_NOT_INITIALIZED; 1.255 + if (NS_WARN_IF(!thread)) 1.256 + return NS_ERROR_INVALID_ARG; 1.257 + 1.258 + nsRefPtr<nsThread> temp; 1.259 + { 1.260 + MutexAutoLock lock(*mLock); 1.261 + mThreadsByPRThread.Get(thread, getter_AddRefs(temp)); 1.262 + } 1.263 + 1.264 + NS_IF_ADDREF(*result = temp); 1.265 + return NS_OK; 1.266 +} 1.267 + 1.268 +NS_IMETHODIMP 1.269 +nsThreadManager::GetMainThread(nsIThread **result) 1.270 +{ 1.271 + // Keep this functioning during Shutdown 1.272 + if (NS_WARN_IF(!mMainThread)) 1.273 + return NS_ERROR_NOT_INITIALIZED; 1.274 + NS_ADDREF(*result = mMainThread); 1.275 + return NS_OK; 1.276 +} 1.277 + 1.278 +NS_IMETHODIMP 1.279 +nsThreadManager::GetCurrentThread(nsIThread **result) 1.280 +{ 1.281 + // Keep this functioning during Shutdown 1.282 + if (NS_WARN_IF(!mMainThread)) 1.283 + return NS_ERROR_NOT_INITIALIZED; 1.284 + *result = GetCurrentThread(); 1.285 + if (!*result) 1.286 + return NS_ERROR_OUT_OF_MEMORY; 1.287 + NS_ADDREF(*result); 1.288 + return NS_OK; 1.289 +} 1.290 + 1.291 +NS_IMETHODIMP 1.292 +nsThreadManager::GetIsMainThread(bool *result) 1.293 +{ 1.294 + // This method may be called post-Shutdown 1.295 + 1.296 + *result = (PR_GetCurrentThread() == mMainPRThread); 1.297 + return NS_OK; 1.298 +} 1.299 + 1.300 +uint32_t 1.301 +nsThreadManager::GetHighestNumberOfThreads() 1.302 +{ 1.303 + MutexAutoLock lock(*mLock); 1.304 + return mHighestNumberOfThreads; 1.305 +}