Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/xpcom/threads/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 12 kB image not shown  

Quelle  AbstractThread.cpp   Sprache: 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 "mozilla/AbstractThread.h"

#include "mozilla/ClearOnShutdown.h"
#include "mozilla/DelayedRunnable.h"
#include "mozilla/Maybe.h"
#include "mozilla/MozPromise.h"  // We initialize the MozPromise logging in this file.
#include "mozilla/ProfilerRunnable.h"
#include "mozilla/StateWatching.h"  // We initialize the StateWatching logging in this file.
#include "mozilla/StaticPtr.h"
#include "mozilla/TaskDispatcher.h"
#include "mozilla/TaskQueue.h"
#include "mozilla/Unused.h"
#include "nsContentUtils.h"
#include "nsIDirectTaskDispatcher.h"
#include "nsIThreadInternal.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadManager.h"
#include "nsThreadUtils.h"
#include <memory>

namespace mozilla {

LazyLogModule gMozPromiseLog("MozPromise");
LazyLogModule gStateWatchingLog("StateWatching");

StaticRefPtr<AbstractThread> sMainThread;
MOZ_THREAD_LOCAL(AbstractThread*) AbstractThread::sCurrentThreadTLS;

class XPCOMThreadWrapper final : public AbstractThread,
                                 public nsIThreadObserver,
                                 public nsIDirectTaskDispatcher {
 public:
  XPCOMThreadWrapper(nsIThreadInternal* aThread, bool aRequireTailDispatch,
                     bool aOnThread)
      : AbstractThread(aRequireTailDispatch),
        mThread(aThread),
        mDirectTaskDispatcher(do_QueryInterface(aThread)),
        mOnThread(aOnThread) {
    MOZ_DIAGNOSTIC_ASSERT(mThread && mDirectTaskDispatcher);
    MOZ_DIAGNOSTIC_ASSERT(!aOnThread || IsCurrentThreadIn());
    if (aOnThread) {
      MOZ_ASSERT(!sCurrentThreadTLS.get(),
                 "There can only be a single XPCOMThreadWrapper available on a "
                 "thread");
      // Set the default current thread so that GetCurrent() never returns
      // nullptr.
      sCurrentThreadTLS.set(this);
    }
  }

  NS_DECL_THREADSAFE_ISUPPORTS

  nsresult Dispatch(already_AddRefed<nsIRunnable> aRunnable,
                    DispatchReason aReason = NormalDispatch) override {
    nsCOMPtr<nsIRunnable> r = aRunnable;
    AbstractThread* currentThread;
    if (aReason != TailDispatch && (currentThread = GetCurrent()) &&
        RequiresTailDispatch(currentThread) &&
        currentThread->IsTailDispatcherAvailable()) {
      return currentThread->TailDispatcher().AddTask(this, r.forget());
    }

    // At a certain point during shutdown, we stop processing events from the
    // main thread event queue (this happens long after all _other_ XPCOM
    // threads have been shut down). However, various bits of subsequent
    // teardown logic (the media shutdown blocker and the final shutdown cycle
    // collection) can trigger state watching and state mirroring notifications
    // that result in dispatch to the main thread. This causes shutdown leaks,
    // because the |Runner| wrapper below creates a guaranteed cycle
    // (Thread->EventQueue->Runnable->Thread) until the event is processed. So
    // if we put the event into a queue that will never be processed, we'll wind
    // up with a leak.
    //
    // We opt to just release the runnable in that case. Ordinarily, this
    // approach could cause problems for runnables that are only safe to be
    // released on the target thread (and not the dispatching thread). This is
    // why XPCOM thread dispatch explicitly leaks the runnable when dispatch
    // fails, rather than releasing it. But given that this condition only
    // applies very late in shutdown when only one thread remains operational,
    // that concern is unlikely to apply.
    if (gXPCOMMainThreadEventsAreDoomed) {
      return NS_ERROR_FAILURE;
    }

    RefPtr<nsIRunnable> runner = new Runner(this, r.forget());
    return mThread->Dispatch(runner.forget(), NS_DISPATCH_NORMAL);
  }

  // Prevent a GCC warning about the other overload of Dispatch being hidden.
  using AbstractThread::Dispatch;

  NS_IMETHOD RegisterShutdownTask(nsITargetShutdownTask* aTask) override {
    return mThread->RegisterShutdownTask(aTask);
  }

  NS_IMETHOD UnregisterShutdownTask(nsITargetShutdownTask* aTask) override {
    return mThread->UnregisterShutdownTask(aTask);
  }

  bool IsCurrentThreadIn() const override {
    return mThread->IsOnCurrentThread();
  }

  TaskDispatcher& TailDispatcher() override {
    MOZ_ASSERT(IsCurrentThreadIn());
    MOZ_ASSERT(IsTailDispatcherAvailable());
    if (!mTailDispatcher) {
      mTailDispatcher =
          std::make_unique<AutoTaskDispatcher>(mDirectTaskDispatcher,
                                               /* aIsTailDispatcher = */ true);
      mThread->AddObserver(this);
    }

    return *mTailDispatcher;
  }

  bool IsTailDispatcherAvailable() override {
    // Our tail dispatching implementation relies on nsIThreadObserver
    // callbacks. If we're not doing event processing, it won't work.
    bool inEventLoop =
        static_cast<nsThread*>(mThread.get())->RecursionDepth() > 0;
    return inEventLoop;
  }

  bool MightHaveTailTasks() override { return !!mTailDispatcher; }

  nsIEventTarget* AsEventTarget() override { return mThread; }

  //-----------------------------------------------------------------------------
  // nsIThreadObserver
  //-----------------------------------------------------------------------------
  NS_IMETHOD OnDispatchedEvent() override { return NS_OK; }

  NS_IMETHOD AfterProcessNextEvent(nsIThreadInternal* thread,
                                   bool eventWasProcessed) override {
    // This is the primary case.
    MaybeFireTailDispatcher();
    return NS_OK;
  }

  NS_IMETHOD OnProcessNextEvent(nsIThreadInternal* thread,
                                bool mayWait) override {
    // In general, the tail dispatcher is handled at the end of the current in
    // AfterProcessNextEvent() above. However, if start spinning a nested event
    // loop, it's generally better to fire the tail dispatcher before the first
    // nested event, rather than after it. This check handles that case.
    MaybeFireTailDispatcher();
    return NS_OK;
  }

  //-----------------------------------------------------------------------------
  // nsIDirectTaskDispatcher
  //-----------------------------------------------------------------------------
  // Forward calls to nsIDirectTaskDispatcher to the underlying nsThread object.
  // We can't use the generated NS_FORWARD_NSIDIRECTTASKDISPATCHER macro
  // as already_AddRefed type must be moved.
  NS_IMETHOD DispatchDirectTask(already_AddRefed<nsIRunnable> aEvent) override {
    return mDirectTaskDispatcher->DispatchDirectTask(std::move(aEvent));
  }
  NS_IMETHOD DrainDirectTasks() override {
    return mDirectTaskDispatcher->DrainDirectTasks();
  }
  NS_IMETHOD HaveDirectTasks(bool* aResult) override {
    return mDirectTaskDispatcher->HaveDirectTasks(aResult);
  }

 private:
  const RefPtr<nsIThreadInternal> mThread;
  const nsCOMPtr<nsIDirectTaskDispatcher> mDirectTaskDispatcher;
  std::unique_ptr<AutoTaskDispatcher> mTailDispatcher;
  const bool mOnThread;

  ~XPCOMThreadWrapper() {
    if (mOnThread) {
      MOZ_DIAGNOSTIC_ASSERT(IsCurrentThreadIn(),
                            "Must be destroyed on the thread it was created");
      sCurrentThreadTLS.set(nullptr);
    }
  }

  void MaybeFireTailDispatcher() {
    if (mTailDispatcher) {
      mTailDispatcher->DrainDirectTasks();
      mThread->RemoveObserver(this);
      mTailDispatcher.reset();
    }
  }

  class Runner : public Runnable {
   public:
    explicit Runner(XPCOMThreadWrapper* aThread,
                    already_AddRefed<nsIRunnable> aRunnable)
        : Runnable("XPCOMThreadWrapper::Runner"),
          mThread(aThread),
          mRunnable(aRunnable) {}

    NS_IMETHOD Run() override {
      MOZ_ASSERT(mThread == AbstractThread::GetCurrent());
      MOZ_ASSERT(mThread->IsCurrentThreadIn());
      SerialEventTargetGuard guard(mThread);
      AUTO_PROFILE_FOLLOWING_RUNNABLE(mRunnable);
      return mRunnable->Run();
    }

#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
    NS_IMETHOD GetName(nsACString& aName) override {
      aName.AssignLiteral("AbstractThread::Runner");
      if (nsCOMPtr<nsINamed> named = do_QueryInterface(mRunnable)) {
        nsAutoCString name;
        named->GetName(name);
        if (!name.IsEmpty()) {
          aName.AppendLiteral(" for ");
          aName.Append(name);
        }
      }
      return NS_OK;
    }
#endif

   private:
    const RefPtr<XPCOMThreadWrapper> mThread;
    const RefPtr<nsIRunnable> mRunnable;
  };
};

NS_IMPL_ISUPPORTS(XPCOMThreadWrapper, nsIThreadObserver,
                  nsIDirectTaskDispatcher, nsISerialEventTarget, nsIEventTarget)

NS_IMETHODIMP_(bool)
AbstractThread::IsOnCurrentThreadInfallible() { return IsCurrentThreadIn(); }

NS_IMETHODIMP
AbstractThread::IsOnCurrentThread(bool* aResult) {
  *aResult = IsCurrentThreadIn();
  return NS_OK;
}

NS_IMETHODIMP
AbstractThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) {
  nsCOMPtr<nsIRunnable> event(aEvent);
  return Dispatch(event.forget(), aFlags);
}

NS_IMETHODIMP
AbstractThread::Dispatch(already_AddRefed<nsIRunnable> aEvent,
                         uint32_t aFlags) {
  return Dispatch(std::move(aEvent), NormalDispatch);
}

NS_IMETHODIMP
AbstractThread::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent,
                                uint32_t aDelayMs) {
  nsCOMPtr<nsIRunnable> event = aEvent;
  NS_ENSURE_TRUE(!!aDelayMs, NS_ERROR_UNEXPECTED);

  RefPtr<DelayedRunnable> r =
      new DelayedRunnable(do_AddRef(this), event.forget(), aDelayMs);
  nsresult rv = r->Init();
  NS_ENSURE_SUCCESS(rv, rv);

  return Dispatch(r.forget(), NS_DISPATCH_NORMAL);
}

nsresult AbstractThread::TailDispatchTasksFor(AbstractThread* aThread) {
  if (MightHaveTailTasks()) {
    return TailDispatcher().DispatchTasksFor(aThread);
  }

  return NS_OK;
}

bool AbstractThread::HasTailTasksFor(AbstractThread* aThread) {
  if (!MightHaveTailTasks()) {
    return false;
  }
  return TailDispatcher().HasTasksFor(aThread);
}

bool AbstractThread::RequiresTailDispatch(AbstractThread* aThread) const {
  MOZ_ASSERT(aThread);
  // We require tail dispatch if both the source and destination
  // threads support it.
  return SupportsTailDispatch() && aThread->SupportsTailDispatch();
}

bool AbstractThread::RequiresTailDispatchFromCurrentThread() const {
  AbstractThread* current = GetCurrent();
  return current && RequiresTailDispatch(current);
}

AbstractThread* AbstractThread::MainThread() {
  MOZ_ASSERT(sMainThread);
  return sMainThread;
}

void AbstractThread::InitTLS() {
  if (!sCurrentThreadTLS.init()) {
    MOZ_CRASH();
  }
}

void AbstractThread::InitMainThread() {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!sMainThread);
  nsCOMPtr<nsIThreadInternal> mainThread =
      do_QueryInterface(nsThreadManager::get().GetMainThreadWeak());
  MOZ_DIAGNOSTIC_ASSERT(mainThread);

  if (!sCurrentThreadTLS.init()) {
    MOZ_CRASH();
  }
  sMainThread = new XPCOMThreadWrapper(mainThread.get(),
                                       /* aRequireTailDispatch = */ true,
                                       true /* onThread */);
}

void AbstractThread::ShutdownMainThread() {
  MOZ_ASSERT(NS_IsMainThread());
  sMainThread = nullptr;
}

void AbstractThread::DispatchStateChange(
    already_AddRefed<nsIRunnable> aRunnable) {
  AbstractThread* currentThread = GetCurrent();
  MOZ_DIAGNOSTIC_ASSERT(currentThread, "An AbstractThread must exist");
  if (currentThread->IsTailDispatcherAvailable()) {
    currentThread->TailDispatcher().AddStateChangeTask(this,
                                                       std::move(aRunnable));
  } else {
    // If the tail dispatcher isn't available, we just avoid sending state
    // updates.
    //
    // This happens, specifically (1) During async shutdown (via the media
    // shutdown blocker), and (2) During the final shutdown cycle collection.
    // Both of these trigger changes to various watched and mirrored state.
    nsCOMPtr<nsIRunnable> neverDispatched = aRunnable;
  }
}

/* static */
void AbstractThread::DispatchDirectTask(
    already_AddRefed<nsIRunnable> aRunnable) {
  AbstractThread* currentThread = GetCurrent();
  MOZ_DIAGNOSTIC_ASSERT(currentThread, "An AbstractThread must exist");
  if (currentThread->IsTailDispatcherAvailable()) {
    currentThread->TailDispatcher().AddDirectTask(std::move(aRunnable));
  } else {
    // If the tail dispatcher isn't available, we post as a regular task.
    currentThread->Dispatch(std::move(aRunnable));
  }
}

}  // namespace mozilla

100%


¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.