mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
8f529f64f5
- Bug 932865 - Add ThreadHangStats for collecting background hang telemetry; r=vladan (2f08a076b)
- Bug 932865 - Add way for telemetry to iterate over active threads; r=froydnj (535615d3d)
- Bug 1128768: Part 3 - Update BHR to allow for hang annotations; r=vladan (0b880a667)
- Bug 935092 - Add ThreadStackHelper to get a thread's pesudo-stack; r=BenWa (1422cfe4d)
- Bug 942488 - Don't report pseudo-stacks without SPS profiler. r=nchen (e160a7a08)
- Bug 946817 - Don't assert mPseudoStack on B2G. r=BenWa (9f846df3b)
- Bug 951431 - Don't get stacks during profiler runs on Linux; r=BenWa (15036e907)
- Bug 978262 - Ignore duplicate frames when getting BHR stack. r=froydnj (964721b1b)
- Bug 985155 - Add signal trampoline on ARM Linux to work around kernel bug. r=snorp (cb8a7846c)
- Bug 995730 - Convert xpcom/threads/ to Gecko style. r=froydnj (fe150404e)
- Bug 1013326 - Distinguish chrome and content scripts in pseudostack; r=snorp (81273c977)
- Bug 1023461 - Remove temporary stack buffer in ThreadStackHelper; r=snorp (cf5a717c2)
- Bug 1023461 - Record filename and line number for chrome JS entries; r=snorp (10c89808f)
- Bug 1022456 - Fix modelines in xpcom/{base,glue,io,string,threads}/. (48dbc0416)
- Bug 1016441 - Switch to using real-time signal in ThreadStackHelper; (2c5f818be)
- Bug 1016629 - b. Use RAII class to assign mStackToFill; r=snorp (769eae130)
- Bug 1016629 - c. Add define for ThreadStackHelper pseudostack support; r=snorp (67def0d2f)
- Bug 1016629 - d. Add and implement GetNativeStack method in ThreadStackHelper; r=snorp r=jseward (46c52f2be)
- Bug 1016629 - e. Implement platform-specific code for filling in context; r=snorp r=jseward (e6a66858b)
- Bug 1016629 - g. Avoid ASan flag when copying stack; r=snorp (0159628b5)
- Bug 1045176 - Unbreak build on non-SPS platforms after bug 1016629. (f1d60d838)
- Bug 1047123 - ThreadStackHelper should use UniquePtr<uint8_t[]>, not ScopedDeleteArray. r=jchen (0e4af313c)
- Bug 1049161 - Fix ThreadStackHelper thread handle permissions on Windows; r=snorp (c05172b1c)
- Bug 1050185 - Make ThreadStackHelper::FillThreadContext Valgrind-friendly. r=nchen (368725774)
- Bug 1050440 - Remove repeated js::RunScript frames in ThreadStackHelper (2a79600b3)
- Bug 1046841 - Fix more style violations in previously touched .cpp files in xpcom/. r=froydnj (02afe2493)
- Bug 1069694 - Remove or move around functions in OldDebugAPI. r=shu (177197302)
- Bug 1069694 - Remove OldDebugAPI from the browser. r=shu (b8c917d42)
- Bug 1100911 - For MacOS builds running on Valgrind, make ThreadStackHelper::GetStack be a no-op. r=nchen. (d99c02e16)
- Bug 1091758 - Report full paths for most chrome scripts; r=snorp (2b72e7878)
- Bug 1109291 - Include better paths for hanging chrome scripts in profile extensions directory; r=snorp r=bsmedberg (1997b9532)
- Bug 1113416 - Don't read stack labels inside hang monitor sighandler; r=nfroyd r=snorp (9688f6069)
- bug 1146027 - more final r=froydnj (7b0f295e5)
- Bug 1164090 - Check for Windows path separator in BHR file name; r=snorp (f014b4d78)
- Bug 1169034 - include <cstdlib> in ThreadStackHelper.cpp to declare correct overload for std::abs; r=jseward (874d4447e)
- Bug 1182996 - Fix and add missing namespace comments. rs=ehsan (054fc00b2)
- Bug 932865 - Collect thread hang stats in BackgroundHangMonitor; (ac80c8e9f)
- minor anticipated fixes to get it compiling (2bd701d15)
361 lines
8.8 KiB
C++
361 lines
8.8 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "CacheIOThread.h"
|
|
#include "CacheFileIOManager.h"
|
|
|
|
#include "nsIRunnable.h"
|
|
#include "nsISupportsImpl.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "mozilla/IOInterposer.h"
|
|
#include "mozilla/VisualEventTracer.h"
|
|
|
|
namespace mozilla {
|
|
namespace net {
|
|
|
|
CacheIOThread* CacheIOThread::sSelf = nullptr;
|
|
|
|
NS_IMPL_ISUPPORTS(CacheIOThread, nsIThreadObserver)
|
|
|
|
CacheIOThread::CacheIOThread()
|
|
: mMonitor("CacheIOThread")
|
|
, mThread(nullptr)
|
|
, mLowestLevelWaiting(LAST_LEVEL)
|
|
, mCurrentlyExecutingLevel(0)
|
|
, mHasXPCOMEvents(false)
|
|
, mRerunCurrentEvent(false)
|
|
, mShutdown(false)
|
|
, mInsideLoop(true)
|
|
{
|
|
sSelf = this;
|
|
}
|
|
|
|
CacheIOThread::~CacheIOThread()
|
|
{
|
|
sSelf = nullptr;
|
|
#ifdef DEBUG
|
|
for (uint32_t level = 0; level < LAST_LEVEL; ++level) {
|
|
MOZ_ASSERT(!mEventQueue[level].Length());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
nsresult CacheIOThread::Init()
|
|
{
|
|
mThread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
|
|
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
|
|
PR_JOINABLE_THREAD, 128 * 1024);
|
|
if (!mThread)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult CacheIOThread::Dispatch(nsIRunnable* aRunnable, uint32_t aLevel)
|
|
{
|
|
NS_ENSURE_ARG(aLevel < LAST_LEVEL);
|
|
|
|
// Runnable is always expected to be non-null, hard null-check bellow.
|
|
MOZ_ASSERT(aRunnable);
|
|
|
|
MonitorAutoLock lock(mMonitor);
|
|
|
|
if (mShutdown && (PR_GetCurrentThread() != mThread))
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
return DispatchInternal(aRunnable, aLevel);
|
|
}
|
|
|
|
nsresult CacheIOThread::DispatchAfterPendingOpens(nsIRunnable* aRunnable)
|
|
{
|
|
// Runnable is always expected to be non-null, hard null-check bellow.
|
|
MOZ_ASSERT(aRunnable);
|
|
|
|
MonitorAutoLock lock(mMonitor);
|
|
|
|
if (mShutdown && (PR_GetCurrentThread() != mThread))
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
// Move everything from later executed OPEN level to the OPEN_PRIORITY level
|
|
// where we post the (eviction) runnable.
|
|
mEventQueue[OPEN_PRIORITY].AppendElements(mEventQueue[OPEN]);
|
|
mEventQueue[OPEN].Clear();
|
|
|
|
return DispatchInternal(aRunnable, OPEN_PRIORITY);
|
|
}
|
|
|
|
nsresult CacheIOThread::DispatchInternal(nsIRunnable* aRunnable, uint32_t aLevel)
|
|
{
|
|
if (NS_WARN_IF(!aRunnable))
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
mMonitor.AssertCurrentThreadOwns();
|
|
|
|
mEventQueue[aLevel].AppendElement(aRunnable);
|
|
if (mLowestLevelWaiting > aLevel)
|
|
mLowestLevelWaiting = aLevel;
|
|
|
|
mMonitor.NotifyAll();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool CacheIOThread::IsCurrentThread()
|
|
{
|
|
return mThread == PR_GetCurrentThread();
|
|
}
|
|
|
|
bool CacheIOThread::YieldInternal()
|
|
{
|
|
if (!IsCurrentThread()) {
|
|
NS_WARNING("Trying to yield to priority events on non-cache2 I/O thread? "
|
|
"You probably do something wrong.");
|
|
return false;
|
|
}
|
|
|
|
if (mCurrentlyExecutingLevel == XPCOM_LEVEL) {
|
|
// Doesn't make any sense, since this handler is the one
|
|
// that would be executed as the next one.
|
|
return false;
|
|
}
|
|
|
|
if (!EventsPending(mCurrentlyExecutingLevel))
|
|
return false;
|
|
|
|
mRerunCurrentEvent = true;
|
|
return true;
|
|
}
|
|
|
|
nsresult CacheIOThread::Shutdown()
|
|
{
|
|
{
|
|
MonitorAutoLock lock(mMonitor);
|
|
mShutdown = true;
|
|
mMonitor.NotifyAll();
|
|
}
|
|
|
|
PR_JoinThread(mThread);
|
|
mThread = nullptr;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
already_AddRefed<nsIEventTarget> CacheIOThread::Target()
|
|
{
|
|
nsCOMPtr<nsIEventTarget> target;
|
|
|
|
target = mXPCOMThread;
|
|
if (!target && mThread)
|
|
{
|
|
MonitorAutoLock lock(mMonitor);
|
|
if (!mXPCOMThread)
|
|
lock.Wait();
|
|
|
|
target = mXPCOMThread;
|
|
}
|
|
|
|
return target.forget();
|
|
}
|
|
|
|
// static
|
|
void CacheIOThread::ThreadFunc(void* aClosure)
|
|
{
|
|
PR_SetCurrentThreadName("Cache2 I/O");
|
|
mozilla::IOInterposer::RegisterCurrentThread();
|
|
CacheIOThread* thread = static_cast<CacheIOThread*>(aClosure);
|
|
thread->ThreadFunc();
|
|
mozilla::IOInterposer::UnregisterCurrentThread();
|
|
}
|
|
|
|
void CacheIOThread::ThreadFunc()
|
|
{
|
|
nsCOMPtr<nsIThreadInternal> threadInternal;
|
|
|
|
{
|
|
MonitorAutoLock lock(mMonitor);
|
|
|
|
// This creates nsThread for this PRThread
|
|
nsCOMPtr<nsIThread> xpcomThread = NS_GetCurrentThread();
|
|
|
|
threadInternal = do_QueryInterface(xpcomThread);
|
|
if (threadInternal)
|
|
threadInternal->SetObserver(this);
|
|
|
|
mXPCOMThread.swap(xpcomThread);
|
|
|
|
lock.NotifyAll();
|
|
|
|
do {
|
|
loopStart:
|
|
// Reset the lowest level now, so that we can detect a new event on
|
|
// a lower level (i.e. higher priority) has been scheduled while
|
|
// executing any previously scheduled event.
|
|
mLowestLevelWaiting = LAST_LEVEL;
|
|
|
|
// Process xpcom events first
|
|
while (mHasXPCOMEvents) {
|
|
eventtracer::AutoEventTracer tracer(this, eventtracer::eExec, eventtracer::eDone,
|
|
"net::cache::io::level(xpcom)");
|
|
|
|
mHasXPCOMEvents = false;
|
|
mCurrentlyExecutingLevel = XPCOM_LEVEL;
|
|
|
|
MonitorAutoUnlock unlock(mMonitor);
|
|
|
|
bool processedEvent;
|
|
nsresult rv;
|
|
do {
|
|
rv = mXPCOMThread->ProcessNextEvent(false, &processedEvent);
|
|
} while (NS_SUCCEEDED(rv) && processedEvent);
|
|
}
|
|
|
|
uint32_t level;
|
|
for (level = 0; level < LAST_LEVEL; ++level) {
|
|
if (!mEventQueue[level].Length()) {
|
|
// no events on this level, go to the next level
|
|
continue;
|
|
}
|
|
|
|
LoopOneLevel(level);
|
|
|
|
// Go to the first (lowest) level again
|
|
goto loopStart;
|
|
}
|
|
|
|
if (EventsPending())
|
|
continue;
|
|
|
|
if (mShutdown)
|
|
break;
|
|
|
|
lock.Wait(PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
if (EventsPending())
|
|
continue;
|
|
|
|
} while (true);
|
|
|
|
MOZ_ASSERT(!EventsPending());
|
|
|
|
// This is for correct assertion on XPCOM events dispatch.
|
|
mInsideLoop = false;
|
|
} // lock
|
|
|
|
if (threadInternal)
|
|
threadInternal->SetObserver(nullptr);
|
|
}
|
|
|
|
static const char* const sLevelTraceName[] = {
|
|
"net::cache::io::level(0)",
|
|
"net::cache::io::level(1)",
|
|
"net::cache::io::level(2)",
|
|
"net::cache::io::level(3)",
|
|
"net::cache::io::level(4)",
|
|
"net::cache::io::level(5)",
|
|
"net::cache::io::level(6)",
|
|
"net::cache::io::level(7)",
|
|
"net::cache::io::level(8)",
|
|
"net::cache::io::level(9)",
|
|
"net::cache::io::level(10)",
|
|
"net::cache::io::level(11)",
|
|
"net::cache::io::level(12)"
|
|
};
|
|
|
|
void CacheIOThread::LoopOneLevel(uint32_t aLevel)
|
|
{
|
|
eventtracer::AutoEventTracer tracer(this, eventtracer::eExec, eventtracer::eDone,
|
|
sLevelTraceName[aLevel]);
|
|
|
|
nsTArray<nsCOMPtr<nsIRunnable> > events;
|
|
events.SwapElements(mEventQueue[aLevel]);
|
|
uint32_t length = events.Length();
|
|
|
|
mCurrentlyExecutingLevel = aLevel;
|
|
|
|
bool returnEvents = false;
|
|
uint32_t index;
|
|
{
|
|
MonitorAutoUnlock unlock(mMonitor);
|
|
|
|
for (index = 0; index < length; ++index) {
|
|
if (EventsPending(aLevel)) {
|
|
// Somebody scheduled a new event on a lower level, break and harry
|
|
// to execute it! Don't forget to return what we haven't exec.
|
|
returnEvents = true;
|
|
break;
|
|
}
|
|
|
|
// Drop any previous flagging, only an event on the current level may set
|
|
// this flag.
|
|
mRerunCurrentEvent = false;
|
|
|
|
events[index]->Run();
|
|
|
|
if (mRerunCurrentEvent) {
|
|
// The event handler yields to higher priority events and wants to rerun.
|
|
returnEvents = true;
|
|
break;
|
|
}
|
|
|
|
// Release outside the lock.
|
|
events[index] = nullptr;
|
|
}
|
|
}
|
|
|
|
if (returnEvents)
|
|
mEventQueue[aLevel].InsertElementsAt(0, events.Elements() + index, length - index);
|
|
}
|
|
|
|
bool CacheIOThread::EventsPending(uint32_t aLastLevel)
|
|
{
|
|
return mLowestLevelWaiting < aLastLevel || mHasXPCOMEvents;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheIOThread::OnDispatchedEvent(nsIThreadInternal *thread)
|
|
{
|
|
MonitorAutoLock lock(mMonitor);
|
|
mHasXPCOMEvents = true;
|
|
MOZ_ASSERT(mInsideLoop);
|
|
lock.Notify();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheIOThread::OnProcessNextEvent(nsIThreadInternal *thread, bool mayWait, uint32_t recursionDepth)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheIOThread::AfterProcessNextEvent(nsIThreadInternal *thread, uint32_t recursionDepth,
|
|
bool eventWasProcessed)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
// Memory reporting
|
|
|
|
size_t CacheIOThread::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
|
{
|
|
MonitorAutoLock lock(const_cast<CacheIOThread*>(this)->mMonitor);
|
|
|
|
size_t n = 0;
|
|
n += mallocSizeOf(mThread);
|
|
for (uint32_t level = 0; level < LAST_LEVEL; ++level) {
|
|
n += mEventQueue[level].SizeOfExcludingThis(mallocSizeOf);
|
|
// Events referenced by the queues are arbitrary objects we cannot be sure
|
|
// are reported elsewhere as well as probably not implementing nsISizeOf
|
|
// interface. Deliberatly omitting them from reporting here.
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
size_t CacheIOThread::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
|
{
|
|
return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
|
|
}
|
|
|
|
} // namespace net
|
|
} // namespace mozilla
|