gecko-dev/xpcom/threads/LazyIdleThread.cpp
Gabriele Svelto 5dc21d568c Bug 1600545 - Remove useless inclusions of header files generated from IDL files in modules/, netwerk/, parser/, security/, startupcache/, storage/, toolkit/, tools/, uriloader/, widget/, xpcom/ and xpfe/ r=Ehsan
The inclusions were removed with the following very crude script and the
resulting breakage was fixed up by hand. The manual fixups did either
revert the changes done by the script, replace a generic header with a more
specific one or replace a header with a forward declaration.

find . -name "*.idl" | grep -v web-platform | grep -v third_party | while read path; do
    interfaces=$(grep "^\(class\|interface\).*:.*" "$path" | cut -d' ' -f2)
    if [ -n "$interfaces" ]; then
        if [[ "$interfaces" == *$'\n'* ]]; then
          regexp="\("
          for i in $interfaces; do regexp="$regexp$i\|"; done
          regexp="${regexp%%\\\|}\)"
        else
          regexp="$interfaces"
        fi
        interface=$(basename "$path")
        rg -l "#include.*${interface%%.idl}.h" . | while read path2; do
            hits=$(grep -v "#include.*${interface%%.idl}.h" "$path2" | grep -c "$regexp" )
            if [ $hits -eq 0 ]; then
                echo "Removing ${interface} from ${path2}"
                grep -v "#include.*${interface%%.idl}.h" "$path2" > "$path2".tmp
                mv -f "$path2".tmp "$path2"
            fi
        done
    fi
done

Differential Revision: https://phabricator.services.mozilla.com/D55444

--HG--
extra : moz-landing-system : lando
2019-12-06 09:17:57 +00:00

629 lines
16 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "LazyIdleThread.h"
#include "nsIObserverService.h"
#include "GeckoProfiler.h"
#include "nsComponentManagerUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "mozilla/Services.h"
#ifdef DEBUG
# define ASSERT_OWNING_THREAD() \
do { \
MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread()); \
} while (0)
#else
# define ASSERT_OWNING_THREAD() /* nothing */
#endif
namespace mozilla {
LazyIdleThread::LazyIdleThread(uint32_t aIdleTimeoutMS, const nsACString& aName,
ShutdownMethod aShutdownMethod,
nsIObserver* aIdleObserver)
: mMutex("LazyIdleThread::mMutex"),
mOwningEventTarget(GetCurrentThreadSerialEventTarget()),
mIdleObserver(aIdleObserver),
mQueuedRunnables(nullptr),
mIdleTimeoutMS(aIdleTimeoutMS),
mPendingEventCount(0),
mIdleNotificationCount(0),
mShutdownMethod(aShutdownMethod),
mShutdown(false),
mThreadIsShuttingDown(false),
mIdleTimeoutEnabled(true),
mName(aName) {
MOZ_ASSERT(mOwningEventTarget, "Need owning thread!");
}
LazyIdleThread::~LazyIdleThread() {
ASSERT_OWNING_THREAD();
Shutdown();
}
void LazyIdleThread::SetWeakIdleObserver(nsIObserver* aObserver) {
ASSERT_OWNING_THREAD();
if (mShutdown) {
NS_WARNING_ASSERTION(!aObserver,
"Setting an observer after Shutdown was called!");
return;
}
mIdleObserver = aObserver;
}
void LazyIdleThread::DisableIdleTimeout() {
ASSERT_OWNING_THREAD();
if (!mIdleTimeoutEnabled) {
return;
}
mIdleTimeoutEnabled = false;
if (mIdleTimer && NS_FAILED(mIdleTimer->Cancel())) {
NS_WARNING("Failed to cancel timer!");
}
MutexAutoLock lock(mMutex);
// Pretend we have a pending event to keep the idle timer from firing.
MOZ_ASSERT(mPendingEventCount < UINT32_MAX, "Way too many!");
mPendingEventCount++;
}
void LazyIdleThread::EnableIdleTimeout() {
ASSERT_OWNING_THREAD();
if (mIdleTimeoutEnabled) {
return;
}
mIdleTimeoutEnabled = true;
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mPendingEventCount, "Mismatched calls to observer methods!");
--mPendingEventCount;
}
if (mThread) {
nsCOMPtr<nsIRunnable> runnable(new Runnable("LazyIdleThreadDummyRunnable"));
if (NS_FAILED(Dispatch(runnable.forget(), NS_DISPATCH_NORMAL))) {
NS_WARNING("Failed to dispatch!");
}
}
}
void LazyIdleThread::PreDispatch() {
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mPendingEventCount < UINT32_MAX, "Way too many!");
mPendingEventCount++;
}
nsresult LazyIdleThread::EnsureThread() {
ASSERT_OWNING_THREAD();
if (mShutdown) {
return NS_ERROR_UNEXPECTED;
}
if (mThread) {
return NS_OK;
}
MOZ_ASSERT(!mPendingEventCount, "Shouldn't have events yet!");
MOZ_ASSERT(!mIdleNotificationCount, "Shouldn't have idle events yet!");
MOZ_ASSERT(!mIdleTimer, "Should have killed this long ago!");
MOZ_ASSERT(!mThreadIsShuttingDown, "Should have cleared that!");
nsresult rv;
if (mShutdownMethod == AutomaticShutdown && NS_IsMainThread()) {
nsCOMPtr<nsIObserverService> obs =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = obs->AddObserver(this, "xpcom-shutdown-threads", false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
mIdleTimer = NS_NewTimer();
if (NS_WARN_IF(!mIdleTimer)) {
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
"LazyIdleThread::InitThread", this, &LazyIdleThread::InitThread);
if (NS_WARN_IF(!runnable)) {
return NS_ERROR_UNEXPECTED;
}
rv = NS_NewNamedThread(mName, getter_AddRefs(mThread), runnable);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
void LazyIdleThread::InitThread() {
// Happens on mThread but mThread may not be set yet...
nsCOMPtr<nsIThreadInternal> thread(do_QueryInterface(NS_GetCurrentThread()));
MOZ_ASSERT(thread, "This should always succeed!");
if (NS_FAILED(thread->SetObserver(this))) {
NS_WARNING("Failed to set thread observer!");
}
}
void LazyIdleThread::CleanupThread() {
nsCOMPtr<nsIThreadInternal> thread(do_QueryInterface(NS_GetCurrentThread()));
MOZ_ASSERT(thread, "This should always succeed!");
if (NS_FAILED(thread->SetObserver(nullptr))) {
NS_WARNING("Failed to set thread observer!");
}
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(!mThreadIsShuttingDown, "Shouldn't be true ever!");
mThreadIsShuttingDown = true;
}
}
void LazyIdleThread::ScheduleTimer() {
ASSERT_OWNING_THREAD();
bool shouldSchedule;
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mIdleNotificationCount, "Should have at least one!");
--mIdleNotificationCount;
shouldSchedule = !mIdleNotificationCount && !mPendingEventCount;
}
if (mIdleTimer) {
if (NS_FAILED(mIdleTimer->Cancel())) {
NS_WARNING("Failed to cancel timer!");
}
if (shouldSchedule && NS_FAILED(mIdleTimer->InitWithCallback(
this, mIdleTimeoutMS, nsITimer::TYPE_ONE_SHOT))) {
NS_WARNING("Failed to schedule timer!");
}
}
}
nsresult LazyIdleThread::ShutdownThread() {
ASSERT_OWNING_THREAD();
// Before calling Shutdown() on the real thread we need to put a queue in
// place in case a runnable is posted to the thread while it's in the
// process of shutting down. This will be our queue.
AutoTArray<nsCOMPtr<nsIRunnable>, 10> queuedRunnables;
nsresult rv;
// Make sure to cancel the shutdown timer before spinning the event loop
// during |mThread->Shutdown()| below. Otherwise the timer might fire and we
// could reenter here.
if (mIdleTimer) {
rv = mIdleTimer->Cancel();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mIdleTimer = nullptr;
}
if (mThread) {
if (mShutdownMethod == AutomaticShutdown && NS_IsMainThread()) {
nsCOMPtr<nsIObserverService> obs =
mozilla::services::GetObserverService();
NS_WARNING_ASSERTION(obs, "Failed to get observer service!");
if (obs &&
NS_FAILED(obs->RemoveObserver(this, "xpcom-shutdown-threads"))) {
NS_WARNING("Failed to remove observer!");
}
}
if (mIdleObserver) {
mIdleObserver->Observe(static_cast<nsIThread*>(this), IDLE_THREAD_TOPIC,
nullptr);
}
#ifdef DEBUG
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(!mThreadIsShuttingDown, "Huh?!");
}
#endif
nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
"LazyIdleThread::CleanupThread", this, &LazyIdleThread::CleanupThread);
if (NS_WARN_IF(!runnable)) {
return NS_ERROR_UNEXPECTED;
}
PreDispatch();
rv = mThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Put the temporary queue in place before calling Shutdown().
mQueuedRunnables = &queuedRunnables;
if (NS_FAILED(mThread->Shutdown())) {
NS_ERROR("Failed to shutdown the thread!");
}
// Now unset the queue.
mQueuedRunnables = nullptr;
mThread = nullptr;
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(!mPendingEventCount, "Huh?!");
MOZ_ASSERT(!mIdleNotificationCount, "Huh?!");
MOZ_ASSERT(mThreadIsShuttingDown, "Huh?!");
mThreadIsShuttingDown = false;
}
}
// If our temporary queue has any runnables then we need to dispatch them.
if (queuedRunnables.Length()) {
// If the thread manager has gone away then these runnables will never run.
if (mShutdown) {
NS_ERROR("Runnables dispatched to LazyIdleThread will never run!");
return NS_OK;
}
// Re-dispatch the queued runnables.
for (uint32_t index = 0; index < queuedRunnables.Length(); index++) {
nsCOMPtr<nsIRunnable> runnable;
runnable.swap(queuedRunnables[index]);
MOZ_ASSERT(runnable, "Null runnable?!");
if (NS_FAILED(Dispatch(runnable.forget(), NS_DISPATCH_NORMAL))) {
NS_ERROR("Failed to re-dispatch queued runnable!");
}
}
}
return NS_OK;
}
void LazyIdleThread::SelfDestruct() {
MOZ_ASSERT(mRefCnt == 1, "Bad refcount!");
delete this;
}
NS_IMPL_ADDREF(LazyIdleThread)
NS_IMETHODIMP_(MozExternalRefCountType)
LazyIdleThread::Release() {
nsrefcnt count = --mRefCnt;
NS_LOG_RELEASE(this, count, "LazyIdleThread");
if (!count) {
// Stabilize refcount.
mRefCnt = 1;
nsCOMPtr<nsIRunnable> runnable = NewNonOwningRunnableMethod(
"LazyIdleThread::SelfDestruct", this, &LazyIdleThread::SelfDestruct);
NS_WARNING_ASSERTION(runnable, "Couldn't make runnable!");
if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
// The only way this could fail is if we're in shutdown, and in that case
// threads should have been joined already. Deleting here isn't dangerous
// anymore because we won't spin the event loop waiting to join the
// thread.
SelfDestruct();
}
}
return count;
}
NS_IMPL_QUERY_INTERFACE(LazyIdleThread, nsIThread, nsIEventTarget,
nsISerialEventTarget, nsITimerCallback,
nsIThreadObserver, nsIObserver, nsINamed)
NS_IMETHODIMP
LazyIdleThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) {
nsCOMPtr<nsIRunnable> event(aEvent);
return Dispatch(event.forget(), aFlags);
}
NS_IMETHODIMP
LazyIdleThread::Dispatch(already_AddRefed<nsIRunnable> aEvent,
uint32_t aFlags) {
ASSERT_OWNING_THREAD();
nsCOMPtr<nsIRunnable> event(aEvent); // avoid leaks
// LazyIdleThread can't always support synchronous dispatch currently.
if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
return NS_ERROR_NOT_IMPLEMENTED;
}
if (NS_WARN_IF(mShutdown)) {
return NS_ERROR_UNEXPECTED;
}
// If our thread is shutting down then we can't actually dispatch right now.
// Queue this runnable for later.
if (UseRunnableQueue()) {
mQueuedRunnables->AppendElement(event);
return NS_OK;
}
nsresult rv = EnsureThread();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
PreDispatch();
return mThread->Dispatch(event.forget(), aFlags);
}
NS_IMETHODIMP
LazyIdleThread::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
LazyIdleThread::GetRunningEventDelay(TimeDuration* aDelay, TimeStamp* aStart) {
if (mThread) {
return mThread->GetRunningEventDelay(aDelay, aStart);
}
*aDelay = TimeDuration();
*aStart = TimeStamp();
return NS_OK;
}
NS_IMETHODIMP
LazyIdleThread::SetRunningEventDelay(TimeDuration aDelay, TimeStamp aStart) {
if (mThread) {
return mThread->SetRunningEventDelay(aDelay, aStart);
}
return NS_OK;
}
NS_IMETHODIMP
LazyIdleThread::IsOnCurrentThread(bool* aIsOnCurrentThread) {
if (mThread) {
return mThread->IsOnCurrentThread(aIsOnCurrentThread);
}
*aIsOnCurrentThread = false;
return NS_OK;
}
NS_IMETHODIMP_(bool)
LazyIdleThread::IsOnCurrentThreadInfallible() {
if (mThread) {
return mThread->IsOnCurrentThread();
}
return false;
}
NS_IMETHODIMP
LazyIdleThread::GetPRThread(PRThread** aPRThread) {
if (mThread) {
return mThread->GetPRThread(aPRThread);
}
*aPRThread = nullptr;
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
LazyIdleThread::GetCanInvokeJS(bool* aCanInvokeJS) {
*aCanInvokeJS = false;
return NS_OK;
}
NS_IMETHODIMP
LazyIdleThread::SetCanInvokeJS(bool aCanInvokeJS) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
LazyIdleThread::GetLastLongTaskEnd(TimeStamp* _retval) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
LazyIdleThread::GetLastLongNonIdleTaskEnd(TimeStamp* _retval) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
LazyIdleThread::SetNameForWakeupTelemetry(const nsACString& aName) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
LazyIdleThread::AsyncShutdown() {
ASSERT_OWNING_THREAD();
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
LazyIdleThread::Shutdown() {
ASSERT_OWNING_THREAD();
mShutdown = true;
nsresult rv = ShutdownThread();
MOZ_ASSERT(!mThread, "Should have destroyed this by now!");
mIdleObserver = nullptr;
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
LazyIdleThread::HasPendingEvents(bool* aHasPendingEvents) {
// This is only supposed to be called from the thread itself so it's not
// implemented here.
MOZ_ASSERT_UNREACHABLE("Shouldn't ever call this!");
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
LazyIdleThread::HasPendingHighPriorityEvents(bool* aHasPendingEvents) {
// This is only supposed to be called from the thread itself so it's not
// implemented here.
MOZ_ASSERT_UNREACHABLE("Shouldn't ever call this!");
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
LazyIdleThread::DispatchToQueue(already_AddRefed<nsIRunnable> aEvent,
EventQueuePriority aQueue) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
LazyIdleThread::ProcessNextEvent(bool aMayWait, bool* aEventWasProcessed) {
// This is only supposed to be called from the thread itself so it's not
// implemented here.
MOZ_ASSERT_UNREACHABLE("Shouldn't ever call this!");
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
LazyIdleThread::Notify(nsITimer* aTimer) {
ASSERT_OWNING_THREAD();
{
MutexAutoLock lock(mMutex);
if (mPendingEventCount || mIdleNotificationCount) {
// Another event was scheduled since this timer was set. Don't do
// anything and wait for the timer to fire again.
return NS_OK;
}
}
nsresult rv = ShutdownThread();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
LazyIdleThread::GetName(nsACString& aName) {
aName.Assign(mName);
return NS_OK;
}
NS_IMETHODIMP
LazyIdleThread::OnDispatchedEvent() {
MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
return NS_OK;
}
NS_IMETHODIMP
LazyIdleThread::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
bool /* aMayWait */) {
return NS_OK;
}
NS_IMETHODIMP
LazyIdleThread::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
bool aEventWasProcessed) {
bool shouldNotifyIdle;
{
MutexAutoLock lock(mMutex);
if (aEventWasProcessed) {
MOZ_ASSERT(mPendingEventCount, "Mismatched calls to observer methods!");
--mPendingEventCount;
}
if (mThreadIsShuttingDown) {
// We're shutting down, no need to fire any timer.
return NS_OK;
}
shouldNotifyIdle = !mPendingEventCount;
if (shouldNotifyIdle) {
MOZ_ASSERT(mIdleNotificationCount < UINT32_MAX, "Way too many!");
mIdleNotificationCount++;
}
}
if (shouldNotifyIdle) {
nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
"LazyIdleThread::ScheduleTimer", this, &LazyIdleThread::ScheduleTimer);
if (NS_WARN_IF(!runnable)) {
return NS_ERROR_UNEXPECTED;
}
nsresult rv =
mOwningEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
return NS_OK;
}
NS_IMETHODIMP
LazyIdleThread::Observe(nsISupports* /* aSubject */, const char* aTopic,
const char16_t* /* aData */) {
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(mShutdownMethod == AutomaticShutdown,
"Should not receive notifications if not AutomaticShutdown!");
MOZ_ASSERT(!strcmp("xpcom-shutdown-threads", aTopic), "Bad topic!");
Shutdown();
return NS_OK;
}
NS_IMETHODIMP
LazyIdleThread::GetEventTarget(nsIEventTarget** aEventTarget) {
nsCOMPtr<nsIEventTarget> target = this;
target.forget(aEventTarget);
return NS_OK;
}
nsIEventTarget* LazyIdleThread::EventTarget() { return this; }
nsISerialEventTarget* LazyIdleThread::SerialEventTarget() { return this; }
} // namespace mozilla