netwerk/cache2/CacheIOThread.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "CacheIOThread.h"
michael@0 6 #include "CacheFileIOManager.h"
michael@0 7
michael@0 8 #include "nsIRunnable.h"
michael@0 9 #include "nsISupportsImpl.h"
michael@0 10 #include "nsPrintfCString.h"
michael@0 11 #include "nsThreadUtils.h"
michael@0 12 #include "mozilla/IOInterposer.h"
michael@0 13 #include "mozilla/VisualEventTracer.h"
michael@0 14
michael@0 15 namespace mozilla {
michael@0 16 namespace net {
michael@0 17
michael@0 18 CacheIOThread* CacheIOThread::sSelf = nullptr;
michael@0 19
michael@0 20 NS_IMPL_ISUPPORTS(CacheIOThread, nsIThreadObserver)
michael@0 21
michael@0 22 CacheIOThread::CacheIOThread()
michael@0 23 : mMonitor("CacheIOThread")
michael@0 24 , mThread(nullptr)
michael@0 25 , mLowestLevelWaiting(LAST_LEVEL)
michael@0 26 , mCurrentlyExecutingLevel(0)
michael@0 27 , mHasXPCOMEvents(false)
michael@0 28 , mRerunCurrentEvent(false)
michael@0 29 , mShutdown(false)
michael@0 30 {
michael@0 31 sSelf = this;
michael@0 32 }
michael@0 33
michael@0 34 CacheIOThread::~CacheIOThread()
michael@0 35 {
michael@0 36 sSelf = nullptr;
michael@0 37 #ifdef DEBUG
michael@0 38 for (uint32_t level = 0; level < LAST_LEVEL; ++level) {
michael@0 39 MOZ_ASSERT(!mEventQueue[level].Length());
michael@0 40 }
michael@0 41 #endif
michael@0 42 }
michael@0 43
michael@0 44 nsresult CacheIOThread::Init()
michael@0 45 {
michael@0 46 mThread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
michael@0 47 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
michael@0 48 PR_JOINABLE_THREAD, 128 * 1024);
michael@0 49 if (!mThread)
michael@0 50 return NS_ERROR_FAILURE;
michael@0 51
michael@0 52 return NS_OK;
michael@0 53 }
michael@0 54
michael@0 55 nsresult CacheIOThread::Dispatch(nsIRunnable* aRunnable, uint32_t aLevel)
michael@0 56 {
michael@0 57 NS_ENSURE_ARG(aLevel < LAST_LEVEL);
michael@0 58
michael@0 59 MonitorAutoLock lock(mMonitor);
michael@0 60
michael@0 61 if (mShutdown && (PR_GetCurrentThread() != mThread))
michael@0 62 return NS_ERROR_UNEXPECTED;
michael@0 63
michael@0 64 return DispatchInternal(aRunnable, aLevel);
michael@0 65 }
michael@0 66
michael@0 67 nsresult CacheIOThread::DispatchAfterPendingOpens(nsIRunnable* aRunnable)
michael@0 68 {
michael@0 69 MonitorAutoLock lock(mMonitor);
michael@0 70
michael@0 71 if (mShutdown && (PR_GetCurrentThread() != mThread))
michael@0 72 return NS_ERROR_UNEXPECTED;
michael@0 73
michael@0 74 // Move everything from later executed OPEN level to the OPEN_PRIORITY level
michael@0 75 // where we post the (eviction) runnable.
michael@0 76 mEventQueue[OPEN_PRIORITY].AppendElements(mEventQueue[OPEN]);
michael@0 77 mEventQueue[OPEN].Clear();
michael@0 78
michael@0 79 return DispatchInternal(aRunnable, OPEN_PRIORITY);
michael@0 80 }
michael@0 81
michael@0 82 nsresult CacheIOThread::DispatchInternal(nsIRunnable* aRunnable, uint32_t aLevel)
michael@0 83 {
michael@0 84 mMonitor.AssertCurrentThreadOwns();
michael@0 85
michael@0 86 mEventQueue[aLevel].AppendElement(aRunnable);
michael@0 87 if (mLowestLevelWaiting > aLevel)
michael@0 88 mLowestLevelWaiting = aLevel;
michael@0 89
michael@0 90 mMonitor.NotifyAll();
michael@0 91
michael@0 92 return NS_OK;
michael@0 93 }
michael@0 94
michael@0 95 bool CacheIOThread::IsCurrentThread()
michael@0 96 {
michael@0 97 return mThread == PR_GetCurrentThread();
michael@0 98 }
michael@0 99
michael@0 100 bool CacheIOThread::YieldInternal()
michael@0 101 {
michael@0 102 if (!IsCurrentThread()) {
michael@0 103 NS_WARNING("Trying to yield to priority events on non-cache2 I/O thread? "
michael@0 104 "You probably do something wrong.");
michael@0 105 return false;
michael@0 106 }
michael@0 107
michael@0 108 if (mCurrentlyExecutingLevel == XPCOM_LEVEL) {
michael@0 109 // Doesn't make any sense, since this handler is the one
michael@0 110 // that would be executed as the next one.
michael@0 111 return false;
michael@0 112 }
michael@0 113
michael@0 114 if (!EventsPending(mCurrentlyExecutingLevel))
michael@0 115 return false;
michael@0 116
michael@0 117 mRerunCurrentEvent = true;
michael@0 118 return true;
michael@0 119 }
michael@0 120
michael@0 121 nsresult CacheIOThread::Shutdown()
michael@0 122 {
michael@0 123 {
michael@0 124 MonitorAutoLock lock(mMonitor);
michael@0 125 mShutdown = true;
michael@0 126 mMonitor.NotifyAll();
michael@0 127 }
michael@0 128
michael@0 129 PR_JoinThread(mThread);
michael@0 130 mThread = nullptr;
michael@0 131
michael@0 132 return NS_OK;
michael@0 133 }
michael@0 134
michael@0 135 already_AddRefed<nsIEventTarget> CacheIOThread::Target()
michael@0 136 {
michael@0 137 nsCOMPtr<nsIEventTarget> target;
michael@0 138
michael@0 139 target = mXPCOMThread;
michael@0 140 if (!target && mThread)
michael@0 141 {
michael@0 142 MonitorAutoLock lock(mMonitor);
michael@0 143 if (!mXPCOMThread)
michael@0 144 lock.Wait();
michael@0 145
michael@0 146 target = mXPCOMThread;
michael@0 147 }
michael@0 148
michael@0 149 return target.forget();
michael@0 150 }
michael@0 151
michael@0 152 // static
michael@0 153 void CacheIOThread::ThreadFunc(void* aClosure)
michael@0 154 {
michael@0 155 PR_SetCurrentThreadName("Cache2 I/O");
michael@0 156 mozilla::IOInterposer::RegisterCurrentThread();
michael@0 157 CacheIOThread* thread = static_cast<CacheIOThread*>(aClosure);
michael@0 158 thread->ThreadFunc();
michael@0 159 mozilla::IOInterposer::UnregisterCurrentThread();
michael@0 160 }
michael@0 161
michael@0 162 void CacheIOThread::ThreadFunc()
michael@0 163 {
michael@0 164 nsCOMPtr<nsIThreadInternal> threadInternal;
michael@0 165
michael@0 166 {
michael@0 167 MonitorAutoLock lock(mMonitor);
michael@0 168
michael@0 169 // This creates nsThread for this PRThread
michael@0 170 nsCOMPtr<nsIThread> xpcomThread = NS_GetCurrentThread();
michael@0 171
michael@0 172 threadInternal = do_QueryInterface(xpcomThread);
michael@0 173 if (threadInternal)
michael@0 174 threadInternal->SetObserver(this);
michael@0 175
michael@0 176 mXPCOMThread.swap(xpcomThread);
michael@0 177
michael@0 178 lock.NotifyAll();
michael@0 179
michael@0 180 do {
michael@0 181 loopStart:
michael@0 182 // Reset the lowest level now, so that we can detect a new event on
michael@0 183 // a lower level (i.e. higher priority) has been scheduled while
michael@0 184 // executing any previously scheduled event.
michael@0 185 mLowestLevelWaiting = LAST_LEVEL;
michael@0 186
michael@0 187 // Process xpcom events first
michael@0 188 while (mHasXPCOMEvents) {
michael@0 189 eventtracer::AutoEventTracer tracer(this, eventtracer::eExec, eventtracer::eDone,
michael@0 190 "net::cache::io::level(xpcom)");
michael@0 191
michael@0 192 mHasXPCOMEvents = false;
michael@0 193 mCurrentlyExecutingLevel = XPCOM_LEVEL;
michael@0 194
michael@0 195 MonitorAutoUnlock unlock(mMonitor);
michael@0 196
michael@0 197 bool processedEvent;
michael@0 198 nsresult rv;
michael@0 199 do {
michael@0 200 rv = mXPCOMThread->ProcessNextEvent(false, &processedEvent);
michael@0 201 } while (NS_SUCCEEDED(rv) && processedEvent);
michael@0 202 }
michael@0 203
michael@0 204 uint32_t level;
michael@0 205 for (level = 0; level < LAST_LEVEL; ++level) {
michael@0 206 if (!mEventQueue[level].Length()) {
michael@0 207 // no events on this level, go to the next level
michael@0 208 continue;
michael@0 209 }
michael@0 210
michael@0 211 LoopOneLevel(level);
michael@0 212
michael@0 213 // Go to the first (lowest) level again
michael@0 214 goto loopStart;
michael@0 215 }
michael@0 216
michael@0 217 if (EventsPending())
michael@0 218 continue;
michael@0 219
michael@0 220 if (mShutdown)
michael@0 221 break;
michael@0 222
michael@0 223 lock.Wait(PR_INTERVAL_NO_TIMEOUT);
michael@0 224
michael@0 225 if (EventsPending())
michael@0 226 continue;
michael@0 227
michael@0 228 } while (true);
michael@0 229
michael@0 230 MOZ_ASSERT(!EventsPending());
michael@0 231 } // lock
michael@0 232
michael@0 233 if (threadInternal)
michael@0 234 threadInternal->SetObserver(nullptr);
michael@0 235 }
michael@0 236
michael@0 237 static const char* const sLevelTraceName[] = {
michael@0 238 "net::cache::io::level(0)",
michael@0 239 "net::cache::io::level(1)",
michael@0 240 "net::cache::io::level(2)",
michael@0 241 "net::cache::io::level(3)",
michael@0 242 "net::cache::io::level(4)",
michael@0 243 "net::cache::io::level(5)",
michael@0 244 "net::cache::io::level(6)",
michael@0 245 "net::cache::io::level(7)",
michael@0 246 "net::cache::io::level(8)",
michael@0 247 "net::cache::io::level(9)",
michael@0 248 "net::cache::io::level(10)",
michael@0 249 "net::cache::io::level(11)",
michael@0 250 "net::cache::io::level(12)"
michael@0 251 };
michael@0 252
michael@0 253 void CacheIOThread::LoopOneLevel(uint32_t aLevel)
michael@0 254 {
michael@0 255 eventtracer::AutoEventTracer tracer(this, eventtracer::eExec, eventtracer::eDone,
michael@0 256 sLevelTraceName[aLevel]);
michael@0 257
michael@0 258 nsTArray<nsRefPtr<nsIRunnable> > events;
michael@0 259 events.SwapElements(mEventQueue[aLevel]);
michael@0 260 uint32_t length = events.Length();
michael@0 261
michael@0 262 mCurrentlyExecutingLevel = aLevel;
michael@0 263
michael@0 264 bool returnEvents = false;
michael@0 265 uint32_t index;
michael@0 266 {
michael@0 267 MonitorAutoUnlock unlock(mMonitor);
michael@0 268
michael@0 269 for (index = 0; index < length; ++index) {
michael@0 270 if (EventsPending(aLevel)) {
michael@0 271 // Somebody scheduled a new event on a lower level, break and harry
michael@0 272 // to execute it! Don't forget to return what we haven't exec.
michael@0 273 returnEvents = true;
michael@0 274 break;
michael@0 275 }
michael@0 276
michael@0 277 // Drop any previous flagging, only an event on the current level may set
michael@0 278 // this flag.
michael@0 279 mRerunCurrentEvent = false;
michael@0 280
michael@0 281 events[index]->Run();
michael@0 282
michael@0 283 if (mRerunCurrentEvent) {
michael@0 284 // The event handler yields to higher priority events and wants to rerun.
michael@0 285 returnEvents = true;
michael@0 286 break;
michael@0 287 }
michael@0 288
michael@0 289 // Release outside the lock.
michael@0 290 events[index] = nullptr;
michael@0 291 }
michael@0 292 }
michael@0 293
michael@0 294 if (returnEvents)
michael@0 295 mEventQueue[aLevel].InsertElementsAt(0, events.Elements() + index, length - index);
michael@0 296 }
michael@0 297
michael@0 298 bool CacheIOThread::EventsPending(uint32_t aLastLevel)
michael@0 299 {
michael@0 300 return mLowestLevelWaiting < aLastLevel || mHasXPCOMEvents;
michael@0 301 }
michael@0 302
michael@0 303 NS_IMETHODIMP CacheIOThread::OnDispatchedEvent(nsIThreadInternal *thread)
michael@0 304 {
michael@0 305 MonitorAutoLock lock(mMonitor);
michael@0 306 mHasXPCOMEvents = true;
michael@0 307 MOZ_ASSERT(!mShutdown || (PR_GetCurrentThread() == mThread));
michael@0 308 lock.Notify();
michael@0 309 return NS_OK;
michael@0 310 }
michael@0 311
michael@0 312 NS_IMETHODIMP CacheIOThread::OnProcessNextEvent(nsIThreadInternal *thread, bool mayWait, uint32_t recursionDepth)
michael@0 313 {
michael@0 314 return NS_OK;
michael@0 315 }
michael@0 316
michael@0 317 NS_IMETHODIMP CacheIOThread::AfterProcessNextEvent(nsIThreadInternal *thread, uint32_t recursionDepth,
michael@0 318 bool eventWasProcessed)
michael@0 319 {
michael@0 320 return NS_OK;
michael@0 321 }
michael@0 322
michael@0 323 // Memory reporting
michael@0 324
michael@0 325 size_t CacheIOThread::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 326 {
michael@0 327 MonitorAutoLock lock(const_cast<CacheIOThread*>(this)->mMonitor);
michael@0 328
michael@0 329 size_t n = 0;
michael@0 330 n += mallocSizeOf(mThread);
michael@0 331 for (uint32_t level = 0; level < LAST_LEVEL; ++level) {
michael@0 332 n += mEventQueue[level].SizeOfExcludingThis(mallocSizeOf);
michael@0 333 // Events referenced by the queues are arbitrary objects we cannot be sure
michael@0 334 // are reported elsewhere as well as probably not implementing nsISizeOf
michael@0 335 // interface. Deliberatly omitting them from reporting here.
michael@0 336 }
michael@0 337
michael@0 338 return n;
michael@0 339 }
michael@0 340
michael@0 341 size_t CacheIOThread::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 342 {
michael@0 343 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
michael@0 344 }
michael@0 345
michael@0 346 } // net
michael@0 347 } // mozilla

mercurial