netwerk/cache2/CacheIOThread.cpp

changeset 0
6474c204b198
     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

mercurial