Bumping manifests a=b2g-bump
[gecko.git] / netwerk / cache2 / CacheIOThread.cpp
blobc6f66bfd2f7349ce934e11ef2b0120503c44d774
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "CacheIOThread.h"
6 #include "CacheFileIOManager.h"
8 #include "nsIRunnable.h"
9 #include "nsISupportsImpl.h"
10 #include "nsPrintfCString.h"
11 #include "nsThreadUtils.h"
12 #include "mozilla/IOInterposer.h"
13 #include "mozilla/VisualEventTracer.h"
15 namespace mozilla {
16 namespace net {
18 CacheIOThread* CacheIOThread::sSelf = nullptr;
20 NS_IMPL_ISUPPORTS(CacheIOThread, nsIThreadObserver)
22 CacheIOThread::CacheIOThread()
23 : mMonitor("CacheIOThread")
24 , mThread(nullptr)
25 , mLowestLevelWaiting(LAST_LEVEL)
26 , mCurrentlyExecutingLevel(0)
27 , mHasXPCOMEvents(false)
28 , mRerunCurrentEvent(false)
29 , mShutdown(false)
31 sSelf = this;
34 CacheIOThread::~CacheIOThread()
36 sSelf = nullptr;
37 #ifdef DEBUG
38 for (uint32_t level = 0; level < LAST_LEVEL; ++level) {
39 MOZ_ASSERT(!mEventQueue[level].Length());
41 #endif
44 nsresult CacheIOThread::Init()
46 mThread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
47 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
48 PR_JOINABLE_THREAD, 128 * 1024);
49 if (!mThread)
50 return NS_ERROR_FAILURE;
52 return NS_OK;
55 nsresult CacheIOThread::Dispatch(nsIRunnable* aRunnable, uint32_t aLevel)
57 NS_ENSURE_ARG(aLevel < LAST_LEVEL);
59 // Runnable is always expected to be non-null, hard null-check bellow.
60 MOZ_ASSERT(aRunnable);
62 MonitorAutoLock lock(mMonitor);
64 if (mShutdown && (PR_GetCurrentThread() != mThread))
65 return NS_ERROR_UNEXPECTED;
67 return DispatchInternal(aRunnable, aLevel);
70 nsresult CacheIOThread::DispatchAfterPendingOpens(nsIRunnable* aRunnable)
72 // Runnable is always expected to be non-null, hard null-check bellow.
73 MOZ_ASSERT(aRunnable);
75 MonitorAutoLock lock(mMonitor);
77 if (mShutdown && (PR_GetCurrentThread() != mThread))
78 return NS_ERROR_UNEXPECTED;
80 // Move everything from later executed OPEN level to the OPEN_PRIORITY level
81 // where we post the (eviction) runnable.
82 mEventQueue[OPEN_PRIORITY].AppendElements(mEventQueue[OPEN]);
83 mEventQueue[OPEN].Clear();
85 return DispatchInternal(aRunnable, OPEN_PRIORITY);
88 nsresult CacheIOThread::DispatchInternal(nsIRunnable* aRunnable, uint32_t aLevel)
90 if (NS_WARN_IF(!aRunnable))
91 return NS_ERROR_NULL_POINTER;
93 mMonitor.AssertCurrentThreadOwns();
95 mEventQueue[aLevel].AppendElement(aRunnable);
96 if (mLowestLevelWaiting > aLevel)
97 mLowestLevelWaiting = aLevel;
99 mMonitor.NotifyAll();
101 return NS_OK;
104 bool CacheIOThread::IsCurrentThread()
106 return mThread == PR_GetCurrentThread();
109 bool CacheIOThread::YieldInternal()
111 if (!IsCurrentThread()) {
112 NS_WARNING("Trying to yield to priority events on non-cache2 I/O thread? "
113 "You probably do something wrong.");
114 return false;
117 if (mCurrentlyExecutingLevel == XPCOM_LEVEL) {
118 // Doesn't make any sense, since this handler is the one
119 // that would be executed as the next one.
120 return false;
123 if (!EventsPending(mCurrentlyExecutingLevel))
124 return false;
126 mRerunCurrentEvent = true;
127 return true;
130 nsresult CacheIOThread::Shutdown()
133 MonitorAutoLock lock(mMonitor);
134 mShutdown = true;
135 mMonitor.NotifyAll();
138 PR_JoinThread(mThread);
139 mThread = nullptr;
141 return NS_OK;
144 already_AddRefed<nsIEventTarget> CacheIOThread::Target()
146 nsCOMPtr<nsIEventTarget> target;
148 target = mXPCOMThread;
149 if (!target && mThread)
151 MonitorAutoLock lock(mMonitor);
152 if (!mXPCOMThread)
153 lock.Wait();
155 target = mXPCOMThread;
158 return target.forget();
161 // static
162 void CacheIOThread::ThreadFunc(void* aClosure)
164 PR_SetCurrentThreadName("Cache2 I/O");
165 mozilla::IOInterposer::RegisterCurrentThread();
166 CacheIOThread* thread = static_cast<CacheIOThread*>(aClosure);
167 thread->ThreadFunc();
168 mozilla::IOInterposer::UnregisterCurrentThread();
171 void CacheIOThread::ThreadFunc()
173 nsCOMPtr<nsIThreadInternal> threadInternal;
176 MonitorAutoLock lock(mMonitor);
178 // This creates nsThread for this PRThread
179 nsCOMPtr<nsIThread> xpcomThread = NS_GetCurrentThread();
181 threadInternal = do_QueryInterface(xpcomThread);
182 if (threadInternal)
183 threadInternal->SetObserver(this);
185 mXPCOMThread.swap(xpcomThread);
187 lock.NotifyAll();
189 do {
190 loopStart:
191 // Reset the lowest level now, so that we can detect a new event on
192 // a lower level (i.e. higher priority) has been scheduled while
193 // executing any previously scheduled event.
194 mLowestLevelWaiting = LAST_LEVEL;
196 // Process xpcom events first
197 while (mHasXPCOMEvents) {
198 eventtracer::AutoEventTracer tracer(this, eventtracer::eExec, eventtracer::eDone,
199 "net::cache::io::level(xpcom)");
201 mHasXPCOMEvents = false;
202 mCurrentlyExecutingLevel = XPCOM_LEVEL;
204 MonitorAutoUnlock unlock(mMonitor);
206 bool processedEvent;
207 nsresult rv;
208 do {
209 rv = mXPCOMThread->ProcessNextEvent(false, &processedEvent);
210 } while (NS_SUCCEEDED(rv) && processedEvent);
213 uint32_t level;
214 for (level = 0; level < LAST_LEVEL; ++level) {
215 if (!mEventQueue[level].Length()) {
216 // no events on this level, go to the next level
217 continue;
220 LoopOneLevel(level);
222 // Go to the first (lowest) level again
223 goto loopStart;
226 if (EventsPending())
227 continue;
229 if (mShutdown)
230 break;
232 lock.Wait(PR_INTERVAL_NO_TIMEOUT);
234 if (EventsPending())
235 continue;
237 } while (true);
239 MOZ_ASSERT(!EventsPending());
240 } // lock
242 if (threadInternal)
243 threadInternal->SetObserver(nullptr);
246 static const char* const sLevelTraceName[] = {
247 "net::cache::io::level(0)",
248 "net::cache::io::level(1)",
249 "net::cache::io::level(2)",
250 "net::cache::io::level(3)",
251 "net::cache::io::level(4)",
252 "net::cache::io::level(5)",
253 "net::cache::io::level(6)",
254 "net::cache::io::level(7)",
255 "net::cache::io::level(8)",
256 "net::cache::io::level(9)",
257 "net::cache::io::level(10)",
258 "net::cache::io::level(11)",
259 "net::cache::io::level(12)"
262 void CacheIOThread::LoopOneLevel(uint32_t aLevel)
264 eventtracer::AutoEventTracer tracer(this, eventtracer::eExec, eventtracer::eDone,
265 sLevelTraceName[aLevel]);
267 nsTArray<nsRefPtr<nsIRunnable> > events;
268 events.SwapElements(mEventQueue[aLevel]);
269 uint32_t length = events.Length();
271 mCurrentlyExecutingLevel = aLevel;
273 bool returnEvents = false;
274 uint32_t index;
276 MonitorAutoUnlock unlock(mMonitor);
278 for (index = 0; index < length; ++index) {
279 if (EventsPending(aLevel)) {
280 // Somebody scheduled a new event on a lower level, break and harry
281 // to execute it! Don't forget to return what we haven't exec.
282 returnEvents = true;
283 break;
286 // Drop any previous flagging, only an event on the current level may set
287 // this flag.
288 mRerunCurrentEvent = false;
290 events[index]->Run();
292 if (mRerunCurrentEvent) {
293 // The event handler yields to higher priority events and wants to rerun.
294 returnEvents = true;
295 break;
298 // Release outside the lock.
299 events[index] = nullptr;
303 if (returnEvents)
304 mEventQueue[aLevel].InsertElementsAt(0, events.Elements() + index, length - index);
307 bool CacheIOThread::EventsPending(uint32_t aLastLevel)
309 return mLowestLevelWaiting < aLastLevel || mHasXPCOMEvents;
312 NS_IMETHODIMP CacheIOThread::OnDispatchedEvent(nsIThreadInternal *thread)
314 MonitorAutoLock lock(mMonitor);
315 mHasXPCOMEvents = true;
316 MOZ_ASSERT(!mShutdown || (PR_GetCurrentThread() == mThread));
317 lock.Notify();
318 return NS_OK;
321 NS_IMETHODIMP CacheIOThread::OnProcessNextEvent(nsIThreadInternal *thread, bool mayWait, uint32_t recursionDepth)
323 return NS_OK;
326 NS_IMETHODIMP CacheIOThread::AfterProcessNextEvent(nsIThreadInternal *thread, uint32_t recursionDepth,
327 bool eventWasProcessed)
329 return NS_OK;
332 // Memory reporting
334 size_t CacheIOThread::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
336 MonitorAutoLock lock(const_cast<CacheIOThread*>(this)->mMonitor);
338 size_t n = 0;
339 n += mallocSizeOf(mThread);
340 for (uint32_t level = 0; level < LAST_LEVEL; ++level) {
341 n += mEventQueue[level].SizeOfExcludingThis(mallocSizeOf);
342 // Events referenced by the queues are arbitrary objects we cannot be sure
343 // are reported elsewhere as well as probably not implementing nsISizeOf
344 // interface. Deliberatly omitting them from reporting here.
347 return n;
350 size_t CacheIOThread::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
352 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
355 } // net
356 } // mozilla