Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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)
30 {
31 sSelf = this;
32 }
34 CacheIOThread::~CacheIOThread()
35 {
36 sSelf = nullptr;
37 #ifdef DEBUG
38 for (uint32_t level = 0; level < LAST_LEVEL; ++level) {
39 MOZ_ASSERT(!mEventQueue[level].Length());
40 }
41 #endif
42 }
44 nsresult CacheIOThread::Init()
45 {
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;
53 }
55 nsresult CacheIOThread::Dispatch(nsIRunnable* aRunnable, uint32_t aLevel)
56 {
57 NS_ENSURE_ARG(aLevel < LAST_LEVEL);
59 MonitorAutoLock lock(mMonitor);
61 if (mShutdown && (PR_GetCurrentThread() != mThread))
62 return NS_ERROR_UNEXPECTED;
64 return DispatchInternal(aRunnable, aLevel);
65 }
67 nsresult CacheIOThread::DispatchAfterPendingOpens(nsIRunnable* aRunnable)
68 {
69 MonitorAutoLock lock(mMonitor);
71 if (mShutdown && (PR_GetCurrentThread() != mThread))
72 return NS_ERROR_UNEXPECTED;
74 // Move everything from later executed OPEN level to the OPEN_PRIORITY level
75 // where we post the (eviction) runnable.
76 mEventQueue[OPEN_PRIORITY].AppendElements(mEventQueue[OPEN]);
77 mEventQueue[OPEN].Clear();
79 return DispatchInternal(aRunnable, OPEN_PRIORITY);
80 }
82 nsresult CacheIOThread::DispatchInternal(nsIRunnable* aRunnable, uint32_t aLevel)
83 {
84 mMonitor.AssertCurrentThreadOwns();
86 mEventQueue[aLevel].AppendElement(aRunnable);
87 if (mLowestLevelWaiting > aLevel)
88 mLowestLevelWaiting = aLevel;
90 mMonitor.NotifyAll();
92 return NS_OK;
93 }
95 bool CacheIOThread::IsCurrentThread()
96 {
97 return mThread == PR_GetCurrentThread();
98 }
100 bool CacheIOThread::YieldInternal()
101 {
102 if (!IsCurrentThread()) {
103 NS_WARNING("Trying to yield to priority events on non-cache2 I/O thread? "
104 "You probably do something wrong.");
105 return false;
106 }
108 if (mCurrentlyExecutingLevel == XPCOM_LEVEL) {
109 // Doesn't make any sense, since this handler is the one
110 // that would be executed as the next one.
111 return false;
112 }
114 if (!EventsPending(mCurrentlyExecutingLevel))
115 return false;
117 mRerunCurrentEvent = true;
118 return true;
119 }
121 nsresult CacheIOThread::Shutdown()
122 {
123 {
124 MonitorAutoLock lock(mMonitor);
125 mShutdown = true;
126 mMonitor.NotifyAll();
127 }
129 PR_JoinThread(mThread);
130 mThread = nullptr;
132 return NS_OK;
133 }
135 already_AddRefed<nsIEventTarget> CacheIOThread::Target()
136 {
137 nsCOMPtr<nsIEventTarget> target;
139 target = mXPCOMThread;
140 if (!target && mThread)
141 {
142 MonitorAutoLock lock(mMonitor);
143 if (!mXPCOMThread)
144 lock.Wait();
146 target = mXPCOMThread;
147 }
149 return target.forget();
150 }
152 // static
153 void CacheIOThread::ThreadFunc(void* aClosure)
154 {
155 PR_SetCurrentThreadName("Cache2 I/O");
156 mozilla::IOInterposer::RegisterCurrentThread();
157 CacheIOThread* thread = static_cast<CacheIOThread*>(aClosure);
158 thread->ThreadFunc();
159 mozilla::IOInterposer::UnregisterCurrentThread();
160 }
162 void CacheIOThread::ThreadFunc()
163 {
164 nsCOMPtr<nsIThreadInternal> threadInternal;
166 {
167 MonitorAutoLock lock(mMonitor);
169 // This creates nsThread for this PRThread
170 nsCOMPtr<nsIThread> xpcomThread = NS_GetCurrentThread();
172 threadInternal = do_QueryInterface(xpcomThread);
173 if (threadInternal)
174 threadInternal->SetObserver(this);
176 mXPCOMThread.swap(xpcomThread);
178 lock.NotifyAll();
180 do {
181 loopStart:
182 // Reset the lowest level now, so that we can detect a new event on
183 // a lower level (i.e. higher priority) has been scheduled while
184 // executing any previously scheduled event.
185 mLowestLevelWaiting = LAST_LEVEL;
187 // Process xpcom events first
188 while (mHasXPCOMEvents) {
189 eventtracer::AutoEventTracer tracer(this, eventtracer::eExec, eventtracer::eDone,
190 "net::cache::io::level(xpcom)");
192 mHasXPCOMEvents = false;
193 mCurrentlyExecutingLevel = XPCOM_LEVEL;
195 MonitorAutoUnlock unlock(mMonitor);
197 bool processedEvent;
198 nsresult rv;
199 do {
200 rv = mXPCOMThread->ProcessNextEvent(false, &processedEvent);
201 } while (NS_SUCCEEDED(rv) && processedEvent);
202 }
204 uint32_t level;
205 for (level = 0; level < LAST_LEVEL; ++level) {
206 if (!mEventQueue[level].Length()) {
207 // no events on this level, go to the next level
208 continue;
209 }
211 LoopOneLevel(level);
213 // Go to the first (lowest) level again
214 goto loopStart;
215 }
217 if (EventsPending())
218 continue;
220 if (mShutdown)
221 break;
223 lock.Wait(PR_INTERVAL_NO_TIMEOUT);
225 if (EventsPending())
226 continue;
228 } while (true);
230 MOZ_ASSERT(!EventsPending());
231 } // lock
233 if (threadInternal)
234 threadInternal->SetObserver(nullptr);
235 }
237 static const char* const sLevelTraceName[] = {
238 "net::cache::io::level(0)",
239 "net::cache::io::level(1)",
240 "net::cache::io::level(2)",
241 "net::cache::io::level(3)",
242 "net::cache::io::level(4)",
243 "net::cache::io::level(5)",
244 "net::cache::io::level(6)",
245 "net::cache::io::level(7)",
246 "net::cache::io::level(8)",
247 "net::cache::io::level(9)",
248 "net::cache::io::level(10)",
249 "net::cache::io::level(11)",
250 "net::cache::io::level(12)"
251 };
253 void CacheIOThread::LoopOneLevel(uint32_t aLevel)
254 {
255 eventtracer::AutoEventTracer tracer(this, eventtracer::eExec, eventtracer::eDone,
256 sLevelTraceName[aLevel]);
258 nsTArray<nsRefPtr<nsIRunnable> > events;
259 events.SwapElements(mEventQueue[aLevel]);
260 uint32_t length = events.Length();
262 mCurrentlyExecutingLevel = aLevel;
264 bool returnEvents = false;
265 uint32_t index;
266 {
267 MonitorAutoUnlock unlock(mMonitor);
269 for (index = 0; index < length; ++index) {
270 if (EventsPending(aLevel)) {
271 // Somebody scheduled a new event on a lower level, break and harry
272 // to execute it! Don't forget to return what we haven't exec.
273 returnEvents = true;
274 break;
275 }
277 // Drop any previous flagging, only an event on the current level may set
278 // this flag.
279 mRerunCurrentEvent = false;
281 events[index]->Run();
283 if (mRerunCurrentEvent) {
284 // The event handler yields to higher priority events and wants to rerun.
285 returnEvents = true;
286 break;
287 }
289 // Release outside the lock.
290 events[index] = nullptr;
291 }
292 }
294 if (returnEvents)
295 mEventQueue[aLevel].InsertElementsAt(0, events.Elements() + index, length - index);
296 }
298 bool CacheIOThread::EventsPending(uint32_t aLastLevel)
299 {
300 return mLowestLevelWaiting < aLastLevel || mHasXPCOMEvents;
301 }
303 NS_IMETHODIMP CacheIOThread::OnDispatchedEvent(nsIThreadInternal *thread)
304 {
305 MonitorAutoLock lock(mMonitor);
306 mHasXPCOMEvents = true;
307 MOZ_ASSERT(!mShutdown || (PR_GetCurrentThread() == mThread));
308 lock.Notify();
309 return NS_OK;
310 }
312 NS_IMETHODIMP CacheIOThread::OnProcessNextEvent(nsIThreadInternal *thread, bool mayWait, uint32_t recursionDepth)
313 {
314 return NS_OK;
315 }
317 NS_IMETHODIMP CacheIOThread::AfterProcessNextEvent(nsIThreadInternal *thread, uint32_t recursionDepth,
318 bool eventWasProcessed)
319 {
320 return NS_OK;
321 }
323 // Memory reporting
325 size_t CacheIOThread::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
326 {
327 MonitorAutoLock lock(const_cast<CacheIOThread*>(this)->mMonitor);
329 size_t n = 0;
330 n += mallocSizeOf(mThread);
331 for (uint32_t level = 0; level < LAST_LEVEL; ++level) {
332 n += mEventQueue[level].SizeOfExcludingThis(mallocSizeOf);
333 // Events referenced by the queues are arbitrary objects we cannot be sure
334 // are reported elsewhere as well as probably not implementing nsISizeOf
335 // interface. Deliberatly omitting them from reporting here.
336 }
338 return n;
339 }
341 size_t CacheIOThread::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
342 {
343 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
344 }
346 } // net
347 } // mozilla