|
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/. */ |
|
4 |
|
5 #include "CacheIOThread.h" |
|
6 #include "CacheFileIOManager.h" |
|
7 |
|
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" |
|
14 |
|
15 namespace mozilla { |
|
16 namespace net { |
|
17 |
|
18 CacheIOThread* CacheIOThread::sSelf = nullptr; |
|
19 |
|
20 NS_IMPL_ISUPPORTS(CacheIOThread, nsIThreadObserver) |
|
21 |
|
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 } |
|
33 |
|
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 } |
|
43 |
|
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; |
|
51 |
|
52 return NS_OK; |
|
53 } |
|
54 |
|
55 nsresult CacheIOThread::Dispatch(nsIRunnable* aRunnable, uint32_t aLevel) |
|
56 { |
|
57 NS_ENSURE_ARG(aLevel < LAST_LEVEL); |
|
58 |
|
59 MonitorAutoLock lock(mMonitor); |
|
60 |
|
61 if (mShutdown && (PR_GetCurrentThread() != mThread)) |
|
62 return NS_ERROR_UNEXPECTED; |
|
63 |
|
64 return DispatchInternal(aRunnable, aLevel); |
|
65 } |
|
66 |
|
67 nsresult CacheIOThread::DispatchAfterPendingOpens(nsIRunnable* aRunnable) |
|
68 { |
|
69 MonitorAutoLock lock(mMonitor); |
|
70 |
|
71 if (mShutdown && (PR_GetCurrentThread() != mThread)) |
|
72 return NS_ERROR_UNEXPECTED; |
|
73 |
|
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(); |
|
78 |
|
79 return DispatchInternal(aRunnable, OPEN_PRIORITY); |
|
80 } |
|
81 |
|
82 nsresult CacheIOThread::DispatchInternal(nsIRunnable* aRunnable, uint32_t aLevel) |
|
83 { |
|
84 mMonitor.AssertCurrentThreadOwns(); |
|
85 |
|
86 mEventQueue[aLevel].AppendElement(aRunnable); |
|
87 if (mLowestLevelWaiting > aLevel) |
|
88 mLowestLevelWaiting = aLevel; |
|
89 |
|
90 mMonitor.NotifyAll(); |
|
91 |
|
92 return NS_OK; |
|
93 } |
|
94 |
|
95 bool CacheIOThread::IsCurrentThread() |
|
96 { |
|
97 return mThread == PR_GetCurrentThread(); |
|
98 } |
|
99 |
|
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 } |
|
107 |
|
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 } |
|
113 |
|
114 if (!EventsPending(mCurrentlyExecutingLevel)) |
|
115 return false; |
|
116 |
|
117 mRerunCurrentEvent = true; |
|
118 return true; |
|
119 } |
|
120 |
|
121 nsresult CacheIOThread::Shutdown() |
|
122 { |
|
123 { |
|
124 MonitorAutoLock lock(mMonitor); |
|
125 mShutdown = true; |
|
126 mMonitor.NotifyAll(); |
|
127 } |
|
128 |
|
129 PR_JoinThread(mThread); |
|
130 mThread = nullptr; |
|
131 |
|
132 return NS_OK; |
|
133 } |
|
134 |
|
135 already_AddRefed<nsIEventTarget> CacheIOThread::Target() |
|
136 { |
|
137 nsCOMPtr<nsIEventTarget> target; |
|
138 |
|
139 target = mXPCOMThread; |
|
140 if (!target && mThread) |
|
141 { |
|
142 MonitorAutoLock lock(mMonitor); |
|
143 if (!mXPCOMThread) |
|
144 lock.Wait(); |
|
145 |
|
146 target = mXPCOMThread; |
|
147 } |
|
148 |
|
149 return target.forget(); |
|
150 } |
|
151 |
|
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 } |
|
161 |
|
162 void CacheIOThread::ThreadFunc() |
|
163 { |
|
164 nsCOMPtr<nsIThreadInternal> threadInternal; |
|
165 |
|
166 { |
|
167 MonitorAutoLock lock(mMonitor); |
|
168 |
|
169 // This creates nsThread for this PRThread |
|
170 nsCOMPtr<nsIThread> xpcomThread = NS_GetCurrentThread(); |
|
171 |
|
172 threadInternal = do_QueryInterface(xpcomThread); |
|
173 if (threadInternal) |
|
174 threadInternal->SetObserver(this); |
|
175 |
|
176 mXPCOMThread.swap(xpcomThread); |
|
177 |
|
178 lock.NotifyAll(); |
|
179 |
|
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; |
|
186 |
|
187 // Process xpcom events first |
|
188 while (mHasXPCOMEvents) { |
|
189 eventtracer::AutoEventTracer tracer(this, eventtracer::eExec, eventtracer::eDone, |
|
190 "net::cache::io::level(xpcom)"); |
|
191 |
|
192 mHasXPCOMEvents = false; |
|
193 mCurrentlyExecutingLevel = XPCOM_LEVEL; |
|
194 |
|
195 MonitorAutoUnlock unlock(mMonitor); |
|
196 |
|
197 bool processedEvent; |
|
198 nsresult rv; |
|
199 do { |
|
200 rv = mXPCOMThread->ProcessNextEvent(false, &processedEvent); |
|
201 } while (NS_SUCCEEDED(rv) && processedEvent); |
|
202 } |
|
203 |
|
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 } |
|
210 |
|
211 LoopOneLevel(level); |
|
212 |
|
213 // Go to the first (lowest) level again |
|
214 goto loopStart; |
|
215 } |
|
216 |
|
217 if (EventsPending()) |
|
218 continue; |
|
219 |
|
220 if (mShutdown) |
|
221 break; |
|
222 |
|
223 lock.Wait(PR_INTERVAL_NO_TIMEOUT); |
|
224 |
|
225 if (EventsPending()) |
|
226 continue; |
|
227 |
|
228 } while (true); |
|
229 |
|
230 MOZ_ASSERT(!EventsPending()); |
|
231 } // lock |
|
232 |
|
233 if (threadInternal) |
|
234 threadInternal->SetObserver(nullptr); |
|
235 } |
|
236 |
|
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 }; |
|
252 |
|
253 void CacheIOThread::LoopOneLevel(uint32_t aLevel) |
|
254 { |
|
255 eventtracer::AutoEventTracer tracer(this, eventtracer::eExec, eventtracer::eDone, |
|
256 sLevelTraceName[aLevel]); |
|
257 |
|
258 nsTArray<nsRefPtr<nsIRunnable> > events; |
|
259 events.SwapElements(mEventQueue[aLevel]); |
|
260 uint32_t length = events.Length(); |
|
261 |
|
262 mCurrentlyExecutingLevel = aLevel; |
|
263 |
|
264 bool returnEvents = false; |
|
265 uint32_t index; |
|
266 { |
|
267 MonitorAutoUnlock unlock(mMonitor); |
|
268 |
|
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 } |
|
276 |
|
277 // Drop any previous flagging, only an event on the current level may set |
|
278 // this flag. |
|
279 mRerunCurrentEvent = false; |
|
280 |
|
281 events[index]->Run(); |
|
282 |
|
283 if (mRerunCurrentEvent) { |
|
284 // The event handler yields to higher priority events and wants to rerun. |
|
285 returnEvents = true; |
|
286 break; |
|
287 } |
|
288 |
|
289 // Release outside the lock. |
|
290 events[index] = nullptr; |
|
291 } |
|
292 } |
|
293 |
|
294 if (returnEvents) |
|
295 mEventQueue[aLevel].InsertElementsAt(0, events.Elements() + index, length - index); |
|
296 } |
|
297 |
|
298 bool CacheIOThread::EventsPending(uint32_t aLastLevel) |
|
299 { |
|
300 return mLowestLevelWaiting < aLastLevel || mHasXPCOMEvents; |
|
301 } |
|
302 |
|
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 } |
|
311 |
|
312 NS_IMETHODIMP CacheIOThread::OnProcessNextEvent(nsIThreadInternal *thread, bool mayWait, uint32_t recursionDepth) |
|
313 { |
|
314 return NS_OK; |
|
315 } |
|
316 |
|
317 NS_IMETHODIMP CacheIOThread::AfterProcessNextEvent(nsIThreadInternal *thread, uint32_t recursionDepth, |
|
318 bool eventWasProcessed) |
|
319 { |
|
320 return NS_OK; |
|
321 } |
|
322 |
|
323 // Memory reporting |
|
324 |
|
325 size_t CacheIOThread::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const |
|
326 { |
|
327 MonitorAutoLock lock(const_cast<CacheIOThread*>(this)->mMonitor); |
|
328 |
|
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 } |
|
337 |
|
338 return n; |
|
339 } |
|
340 |
|
341 size_t CacheIOThread::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const |
|
342 { |
|
343 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf); |
|
344 } |
|
345 |
|
346 } // net |
|
347 } // mozilla |