1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/cache2/CacheIOThread.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,347 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "CacheIOThread.h" 1.9 +#include "CacheFileIOManager.h" 1.10 + 1.11 +#include "nsIRunnable.h" 1.12 +#include "nsISupportsImpl.h" 1.13 +#include "nsPrintfCString.h" 1.14 +#include "nsThreadUtils.h" 1.15 +#include "mozilla/IOInterposer.h" 1.16 +#include "mozilla/VisualEventTracer.h" 1.17 + 1.18 +namespace mozilla { 1.19 +namespace net { 1.20 + 1.21 +CacheIOThread* CacheIOThread::sSelf = nullptr; 1.22 + 1.23 +NS_IMPL_ISUPPORTS(CacheIOThread, nsIThreadObserver) 1.24 + 1.25 +CacheIOThread::CacheIOThread() 1.26 +: mMonitor("CacheIOThread") 1.27 +, mThread(nullptr) 1.28 +, mLowestLevelWaiting(LAST_LEVEL) 1.29 +, mCurrentlyExecutingLevel(0) 1.30 +, mHasXPCOMEvents(false) 1.31 +, mRerunCurrentEvent(false) 1.32 +, mShutdown(false) 1.33 +{ 1.34 + sSelf = this; 1.35 +} 1.36 + 1.37 +CacheIOThread::~CacheIOThread() 1.38 +{ 1.39 + sSelf = nullptr; 1.40 +#ifdef DEBUG 1.41 + for (uint32_t level = 0; level < LAST_LEVEL; ++level) { 1.42 + MOZ_ASSERT(!mEventQueue[level].Length()); 1.43 + } 1.44 +#endif 1.45 +} 1.46 + 1.47 +nsresult CacheIOThread::Init() 1.48 +{ 1.49 + mThread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this, 1.50 + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, 1.51 + PR_JOINABLE_THREAD, 128 * 1024); 1.52 + if (!mThread) 1.53 + return NS_ERROR_FAILURE; 1.54 + 1.55 + return NS_OK; 1.56 +} 1.57 + 1.58 +nsresult CacheIOThread::Dispatch(nsIRunnable* aRunnable, uint32_t aLevel) 1.59 +{ 1.60 + NS_ENSURE_ARG(aLevel < LAST_LEVEL); 1.61 + 1.62 + MonitorAutoLock lock(mMonitor); 1.63 + 1.64 + if (mShutdown && (PR_GetCurrentThread() != mThread)) 1.65 + return NS_ERROR_UNEXPECTED; 1.66 + 1.67 + return DispatchInternal(aRunnable, aLevel); 1.68 +} 1.69 + 1.70 +nsresult CacheIOThread::DispatchAfterPendingOpens(nsIRunnable* aRunnable) 1.71 +{ 1.72 + MonitorAutoLock lock(mMonitor); 1.73 + 1.74 + if (mShutdown && (PR_GetCurrentThread() != mThread)) 1.75 + return NS_ERROR_UNEXPECTED; 1.76 + 1.77 + // Move everything from later executed OPEN level to the OPEN_PRIORITY level 1.78 + // where we post the (eviction) runnable. 1.79 + mEventQueue[OPEN_PRIORITY].AppendElements(mEventQueue[OPEN]); 1.80 + mEventQueue[OPEN].Clear(); 1.81 + 1.82 + return DispatchInternal(aRunnable, OPEN_PRIORITY); 1.83 +} 1.84 + 1.85 +nsresult CacheIOThread::DispatchInternal(nsIRunnable* aRunnable, uint32_t aLevel) 1.86 +{ 1.87 + mMonitor.AssertCurrentThreadOwns(); 1.88 + 1.89 + mEventQueue[aLevel].AppendElement(aRunnable); 1.90 + if (mLowestLevelWaiting > aLevel) 1.91 + mLowestLevelWaiting = aLevel; 1.92 + 1.93 + mMonitor.NotifyAll(); 1.94 + 1.95 + return NS_OK; 1.96 +} 1.97 + 1.98 +bool CacheIOThread::IsCurrentThread() 1.99 +{ 1.100 + return mThread == PR_GetCurrentThread(); 1.101 +} 1.102 + 1.103 +bool CacheIOThread::YieldInternal() 1.104 +{ 1.105 + if (!IsCurrentThread()) { 1.106 + NS_WARNING("Trying to yield to priority events on non-cache2 I/O thread? " 1.107 + "You probably do something wrong."); 1.108 + return false; 1.109 + } 1.110 + 1.111 + if (mCurrentlyExecutingLevel == XPCOM_LEVEL) { 1.112 + // Doesn't make any sense, since this handler is the one 1.113 + // that would be executed as the next one. 1.114 + return false; 1.115 + } 1.116 + 1.117 + if (!EventsPending(mCurrentlyExecutingLevel)) 1.118 + return false; 1.119 + 1.120 + mRerunCurrentEvent = true; 1.121 + return true; 1.122 +} 1.123 + 1.124 +nsresult CacheIOThread::Shutdown() 1.125 +{ 1.126 + { 1.127 + MonitorAutoLock lock(mMonitor); 1.128 + mShutdown = true; 1.129 + mMonitor.NotifyAll(); 1.130 + } 1.131 + 1.132 + PR_JoinThread(mThread); 1.133 + mThread = nullptr; 1.134 + 1.135 + return NS_OK; 1.136 +} 1.137 + 1.138 +already_AddRefed<nsIEventTarget> CacheIOThread::Target() 1.139 +{ 1.140 + nsCOMPtr<nsIEventTarget> target; 1.141 + 1.142 + target = mXPCOMThread; 1.143 + if (!target && mThread) 1.144 + { 1.145 + MonitorAutoLock lock(mMonitor); 1.146 + if (!mXPCOMThread) 1.147 + lock.Wait(); 1.148 + 1.149 + target = mXPCOMThread; 1.150 + } 1.151 + 1.152 + return target.forget(); 1.153 +} 1.154 + 1.155 +// static 1.156 +void CacheIOThread::ThreadFunc(void* aClosure) 1.157 +{ 1.158 + PR_SetCurrentThreadName("Cache2 I/O"); 1.159 + mozilla::IOInterposer::RegisterCurrentThread(); 1.160 + CacheIOThread* thread = static_cast<CacheIOThread*>(aClosure); 1.161 + thread->ThreadFunc(); 1.162 + mozilla::IOInterposer::UnregisterCurrentThread(); 1.163 +} 1.164 + 1.165 +void CacheIOThread::ThreadFunc() 1.166 +{ 1.167 + nsCOMPtr<nsIThreadInternal> threadInternal; 1.168 + 1.169 + { 1.170 + MonitorAutoLock lock(mMonitor); 1.171 + 1.172 + // This creates nsThread for this PRThread 1.173 + nsCOMPtr<nsIThread> xpcomThread = NS_GetCurrentThread(); 1.174 + 1.175 + threadInternal = do_QueryInterface(xpcomThread); 1.176 + if (threadInternal) 1.177 + threadInternal->SetObserver(this); 1.178 + 1.179 + mXPCOMThread.swap(xpcomThread); 1.180 + 1.181 + lock.NotifyAll(); 1.182 + 1.183 + do { 1.184 +loopStart: 1.185 + // Reset the lowest level now, so that we can detect a new event on 1.186 + // a lower level (i.e. higher priority) has been scheduled while 1.187 + // executing any previously scheduled event. 1.188 + mLowestLevelWaiting = LAST_LEVEL; 1.189 + 1.190 + // Process xpcom events first 1.191 + while (mHasXPCOMEvents) { 1.192 + eventtracer::AutoEventTracer tracer(this, eventtracer::eExec, eventtracer::eDone, 1.193 + "net::cache::io::level(xpcom)"); 1.194 + 1.195 + mHasXPCOMEvents = false; 1.196 + mCurrentlyExecutingLevel = XPCOM_LEVEL; 1.197 + 1.198 + MonitorAutoUnlock unlock(mMonitor); 1.199 + 1.200 + bool processedEvent; 1.201 + nsresult rv; 1.202 + do { 1.203 + rv = mXPCOMThread->ProcessNextEvent(false, &processedEvent); 1.204 + } while (NS_SUCCEEDED(rv) && processedEvent); 1.205 + } 1.206 + 1.207 + uint32_t level; 1.208 + for (level = 0; level < LAST_LEVEL; ++level) { 1.209 + if (!mEventQueue[level].Length()) { 1.210 + // no events on this level, go to the next level 1.211 + continue; 1.212 + } 1.213 + 1.214 + LoopOneLevel(level); 1.215 + 1.216 + // Go to the first (lowest) level again 1.217 + goto loopStart; 1.218 + } 1.219 + 1.220 + if (EventsPending()) 1.221 + continue; 1.222 + 1.223 + if (mShutdown) 1.224 + break; 1.225 + 1.226 + lock.Wait(PR_INTERVAL_NO_TIMEOUT); 1.227 + 1.228 + if (EventsPending()) 1.229 + continue; 1.230 + 1.231 + } while (true); 1.232 + 1.233 + MOZ_ASSERT(!EventsPending()); 1.234 + } // lock 1.235 + 1.236 + if (threadInternal) 1.237 + threadInternal->SetObserver(nullptr); 1.238 +} 1.239 + 1.240 +static const char* const sLevelTraceName[] = { 1.241 + "net::cache::io::level(0)", 1.242 + "net::cache::io::level(1)", 1.243 + "net::cache::io::level(2)", 1.244 + "net::cache::io::level(3)", 1.245 + "net::cache::io::level(4)", 1.246 + "net::cache::io::level(5)", 1.247 + "net::cache::io::level(6)", 1.248 + "net::cache::io::level(7)", 1.249 + "net::cache::io::level(8)", 1.250 + "net::cache::io::level(9)", 1.251 + "net::cache::io::level(10)", 1.252 + "net::cache::io::level(11)", 1.253 + "net::cache::io::level(12)" 1.254 +}; 1.255 + 1.256 +void CacheIOThread::LoopOneLevel(uint32_t aLevel) 1.257 +{ 1.258 + eventtracer::AutoEventTracer tracer(this, eventtracer::eExec, eventtracer::eDone, 1.259 + sLevelTraceName[aLevel]); 1.260 + 1.261 + nsTArray<nsRefPtr<nsIRunnable> > events; 1.262 + events.SwapElements(mEventQueue[aLevel]); 1.263 + uint32_t length = events.Length(); 1.264 + 1.265 + mCurrentlyExecutingLevel = aLevel; 1.266 + 1.267 + bool returnEvents = false; 1.268 + uint32_t index; 1.269 + { 1.270 + MonitorAutoUnlock unlock(mMonitor); 1.271 + 1.272 + for (index = 0; index < length; ++index) { 1.273 + if (EventsPending(aLevel)) { 1.274 + // Somebody scheduled a new event on a lower level, break and harry 1.275 + // to execute it! Don't forget to return what we haven't exec. 1.276 + returnEvents = true; 1.277 + break; 1.278 + } 1.279 + 1.280 + // Drop any previous flagging, only an event on the current level may set 1.281 + // this flag. 1.282 + mRerunCurrentEvent = false; 1.283 + 1.284 + events[index]->Run(); 1.285 + 1.286 + if (mRerunCurrentEvent) { 1.287 + // The event handler yields to higher priority events and wants to rerun. 1.288 + returnEvents = true; 1.289 + break; 1.290 + } 1.291 + 1.292 + // Release outside the lock. 1.293 + events[index] = nullptr; 1.294 + } 1.295 + } 1.296 + 1.297 + if (returnEvents) 1.298 + mEventQueue[aLevel].InsertElementsAt(0, events.Elements() + index, length - index); 1.299 +} 1.300 + 1.301 +bool CacheIOThread::EventsPending(uint32_t aLastLevel) 1.302 +{ 1.303 + return mLowestLevelWaiting < aLastLevel || mHasXPCOMEvents; 1.304 +} 1.305 + 1.306 +NS_IMETHODIMP CacheIOThread::OnDispatchedEvent(nsIThreadInternal *thread) 1.307 +{ 1.308 + MonitorAutoLock lock(mMonitor); 1.309 + mHasXPCOMEvents = true; 1.310 + MOZ_ASSERT(!mShutdown || (PR_GetCurrentThread() == mThread)); 1.311 + lock.Notify(); 1.312 + return NS_OK; 1.313 +} 1.314 + 1.315 +NS_IMETHODIMP CacheIOThread::OnProcessNextEvent(nsIThreadInternal *thread, bool mayWait, uint32_t recursionDepth) 1.316 +{ 1.317 + return NS_OK; 1.318 +} 1.319 + 1.320 +NS_IMETHODIMP CacheIOThread::AfterProcessNextEvent(nsIThreadInternal *thread, uint32_t recursionDepth, 1.321 + bool eventWasProcessed) 1.322 +{ 1.323 + return NS_OK; 1.324 +} 1.325 + 1.326 +// Memory reporting 1.327 + 1.328 +size_t CacheIOThread::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const 1.329 +{ 1.330 + MonitorAutoLock lock(const_cast<CacheIOThread*>(this)->mMonitor); 1.331 + 1.332 + size_t n = 0; 1.333 + n += mallocSizeOf(mThread); 1.334 + for (uint32_t level = 0; level < LAST_LEVEL; ++level) { 1.335 + n += mEventQueue[level].SizeOfExcludingThis(mallocSizeOf); 1.336 + // Events referenced by the queues are arbitrary objects we cannot be sure 1.337 + // are reported elsewhere as well as probably not implementing nsISizeOf 1.338 + // interface. Deliberatly omitting them from reporting here. 1.339 + } 1.340 + 1.341 + return n; 1.342 +} 1.343 + 1.344 +size_t CacheIOThread::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const 1.345 +{ 1.346 + return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf); 1.347 +} 1.348 + 1.349 +} // net 1.350 +} // mozilla