netwerk/cache2/CacheIOThread.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:2dcc60422349
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

mercurial