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"
18 CacheIOThread
* CacheIOThread::sSelf
= nullptr;
20 NS_IMPL_ISUPPORTS(CacheIOThread
, nsIThreadObserver
)
22 CacheIOThread::CacheIOThread()
23 : mMonitor("CacheIOThread")
25 , mLowestLevelWaiting(LAST_LEVEL
)
26 , mCurrentlyExecutingLevel(0)
27 , mHasXPCOMEvents(false)
28 , mRerunCurrentEvent(false)
34 CacheIOThread::~CacheIOThread()
38 for (uint32_t level
= 0; level
< LAST_LEVEL
; ++level
) {
39 MOZ_ASSERT(!mEventQueue
[level
].Length());
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);
50 return NS_ERROR_FAILURE
;
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
;
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.");
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.
123 if (!EventsPending(mCurrentlyExecutingLevel
))
126 mRerunCurrentEvent
= true;
130 nsresult
CacheIOThread::Shutdown()
133 MonitorAutoLock
lock(mMonitor
);
135 mMonitor
.NotifyAll();
138 PR_JoinThread(mThread
);
144 already_AddRefed
<nsIEventTarget
> CacheIOThread::Target()
146 nsCOMPtr
<nsIEventTarget
> target
;
148 target
= mXPCOMThread
;
149 if (!target
&& mThread
)
151 MonitorAutoLock
lock(mMonitor
);
155 target
= mXPCOMThread
;
158 return target
.forget();
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
);
183 threadInternal
->SetObserver(this);
185 mXPCOMThread
.swap(xpcomThread
);
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
);
209 rv
= mXPCOMThread
->ProcessNextEvent(false, &processedEvent
);
210 } while (NS_SUCCEEDED(rv
) && processedEvent
);
214 for (level
= 0; level
< LAST_LEVEL
; ++level
) {
215 if (!mEventQueue
[level
].Length()) {
216 // no events on this level, go to the next level
222 // Go to the first (lowest) level again
232 lock
.Wait(PR_INTERVAL_NO_TIMEOUT
);
239 MOZ_ASSERT(!EventsPending());
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;
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.
286 // Drop any previous flagging, only an event on the current level may set
288 mRerunCurrentEvent
= false;
290 events
[index
]->Run();
292 if (mRerunCurrentEvent
) {
293 // The event handler yields to higher priority events and wants to rerun.
298 // Release outside the lock.
299 events
[index
] = nullptr;
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
));
321 NS_IMETHODIMP
CacheIOThread::OnProcessNextEvent(nsIThreadInternal
*thread
, bool mayWait
, uint32_t recursionDepth
)
326 NS_IMETHODIMP
CacheIOThread::AfterProcessNextEvent(nsIThreadInternal
*thread
, uint32_t recursionDepth
,
327 bool eventWasProcessed
)
334 size_t CacheIOThread::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
336 MonitorAutoLock
lock(const_cast<CacheIOThread
*>(this)->mMonitor
);
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.
350 size_t CacheIOThread::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
352 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf
);