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 9 kB image not shown  

Quelle  ThreadEventQueue.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/ThreadEventQueue.h"
#include "mozilla/EventQueue.h"

#include "LeakRefPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsITargetShutdownTask.h"
#include "nsIThreadInternal.h"
#include "nsThreadUtils.h"
#include "nsThread.h"
#include "ThreadEventTarget.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/TaskController.h"
#include "mozilla/StaticPrefs_threads.h"

using namespace mozilla;

class ThreadEventQueue::NestedSink : public ThreadTargetSink {
 public:
  NestedSink(EventQueue* aQueue, ThreadEventQueue* aOwner)
      : mQueue(aQueue), mOwner(aOwner) {}

  bool PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
                EventQueuePriority aPriority) final {
    return mOwner->PutEventInternal(std::move(aEvent), aPriority, this);
  }

  void Disconnect(const MutexAutoLock& aProofOfLock) final { mQueue = nullptr; }

  nsresult RegisterShutdownTask(nsITargetShutdownTask* aTask) final {
    return NS_ERROR_NOT_IMPLEMENTED;
  }
  nsresult UnregisterShutdownTask(nsITargetShutdownTask* aTask) final {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
    if (mQueue) {
      return mQueue->SizeOfIncludingThis(aMallocSizeOf);
    }
    return 0;
  }

 private:
  friend class ThreadEventQueue;

  // This is a non-owning reference. It must live at least until Disconnect is
  // called to clear it out.
  EventQueue* mQueue;
  RefPtr<ThreadEventQueue> mOwner;
};

ThreadEventQueue::ThreadEventQueue(UniquePtr<EventQueue> aQueue,
                                   bool aIsMainThread)
    : mBaseQueue(std::move(aQueue)),
      mLock("ThreadEventQueue"),
      mEventsAvailable(mLock, "EventsAvail"),
      mIsMainThread(aIsMainThread) {
  if (aIsMainThread) {
    TaskController::Get()->SetConditionVariable(&mEventsAvailable);
  }
}

ThreadEventQueue::~ThreadEventQueue() { MOZ_ASSERT(mNestedQueues.IsEmpty()); }

bool ThreadEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
                                EventQueuePriority aPriority) {
  return PutEventInternal(std::move(aEvent), aPriority, nullptr);
}

bool ThreadEventQueue::PutEventInternal(already_AddRefed<nsIRunnable>&& aEvent,
                                        EventQueuePriority aPriority,
                                        NestedSink* aSink) {
  // We want to leak the reference when we fail to dispatch it, so that
  // we won't release the event in a wrong thread.
  LeakRefPtr<nsIRunnable> event(std::move(aEvent));
  nsCOMPtr<nsIThreadObserver> obs;

  {
    // Check if the runnable wants to override the passed-in priority.
    // Do this outside the lock, so runnables implemented in JS can QI
    // (and possibly GC) outside of the lock.
    if (mIsMainThread) {
      auto* e = event.get();  // can't do_QueryInterface on LeakRefPtr.
      if (nsCOMPtr<nsIRunnablePriority> runnablePrio = do_QueryInterface(e)) {
        uint32_t prio = nsIRunnablePriority::PRIORITY_NORMAL;
        runnablePrio->GetPriority(&prio);
        if (prio == nsIRunnablePriority::PRIORITY_CONTROL) {
          aPriority = EventQueuePriority::Control;
        } else if (prio == nsIRunnablePriority::PRIORITY_RENDER_BLOCKING) {
          aPriority = EventQueuePriority::RenderBlocking;
        } else if (prio == nsIRunnablePriority::PRIORITY_VSYNC) {
          aPriority = EventQueuePriority::Vsync;
        } else if (prio == nsIRunnablePriority::PRIORITY_INPUT_HIGH) {
          aPriority = EventQueuePriority::InputHigh;
        } else if (prio == nsIRunnablePriority::PRIORITY_MEDIUMHIGH) {
          aPriority = EventQueuePriority::MediumHigh;
        } else if (prio == nsIRunnablePriority::PRIORITY_DEFERRED_TIMERS) {
          aPriority = EventQueuePriority::DeferredTimers;
        } else if (prio == nsIRunnablePriority::PRIORITY_IDLE) {
          aPriority = EventQueuePriority::Idle;
        } else if (prio == nsIRunnablePriority::PRIORITY_LOW) {
          aPriority = EventQueuePriority::Low;
        }
      }
    }

    MutexAutoLock lock(mLock);

    if (mEventsAreDoomed) {
      return false;
    }

    if (aSink) {
      if (!aSink->mQueue) {
        return false;
      }

      aSink->mQueue->PutEvent(event.take(), aPriority, lock);
    } else {
      mBaseQueue->PutEvent(event.take(), aPriority, lock);
    }

    mEventsAvailable.Notify();

    // Make sure to grab the observer before dropping the lock, otherwise the
    // event that we just placed into the queue could run and eventually delete
    // this nsThread before the calling thread is scheduled again. We would then
    // crash while trying to access a dead nsThread.
    obs = mObserver;
  }

  if (obs) {
    obs->OnDispatchedEvent();
  }

  return true;
}

already_AddRefed<nsIRunnable> ThreadEventQueue::GetEvent(
    bool aMayWait, mozilla::TimeDuration* aLastEventDelay) {
  nsCOMPtr<nsIRunnable> event;
  {
    // Scope for lock.  When we are about to return, we will exit this
    // scope so we can do some work after releasing the lock but
    // before returning.
    MutexAutoLock lock(mLock);

    for (;;) {
      const bool noNestedQueue = mNestedQueues.IsEmpty();
      if (noNestedQueue) {
        event = mBaseQueue->GetEvent(lock, aLastEventDelay);
      } else {
        // We always get events from the topmost queue when there are nested
        // queues.
        event =
            mNestedQueues.LastElement().mQueue->GetEvent(lock, aLastEventDelay);
      }

      if (event) {
        break;
      }

      // No runnable available.  Sleep waiting for one if if we're supposed to.
      // Otherwise just go ahead and return null.
      if (!aMayWait) {
        break;
      }

      AUTO_PROFILER_LABEL("ThreadEventQueue::GetEvent::Wait", IDLE);
      mEventsAvailable.Wait();
    }
  }

  return event.forget();
}

bool ThreadEventQueue::HasPendingEvent() {
  MutexAutoLock lock(mLock);

  // We always get events from the topmost queue when there are nested queues.
  if (mNestedQueues.IsEmpty()) {
    return mBaseQueue->HasReadyEvent(lock);
  } else {
    return mNestedQueues.LastElement().mQueue->HasReadyEvent(lock);
  }
}

bool ThreadEventQueue::ShutdownIfNoPendingEvents() {
  MutexAutoLock lock(mLock);
  if (mNestedQueues.IsEmpty() && mBaseQueue->IsEmpty(lock)) {
    mEventsAreDoomed = true;
    return true;
  }
  return false;
}

already_AddRefed<nsISerialEventTarget> ThreadEventQueue::PushEventQueue() {
  auto queue = MakeUnique<EventQueue>();
  RefPtr<NestedSink> sink = new NestedSink(queue.get(), this);
  RefPtr<ThreadEventTarget> eventTarget =
      new ThreadEventTarget(sink, NS_IsMainThread(), false);

  MutexAutoLock lock(mLock);

  mNestedQueues.AppendElement(NestedQueueItem(std::move(queue), eventTarget));
  return eventTarget.forget();
}

void ThreadEventQueue::PopEventQueue(nsIEventTarget* aTarget) {
  MutexAutoLock lock(mLock);

  MOZ_ASSERT(!mNestedQueues.IsEmpty());

  NestedQueueItem& item = mNestedQueues.LastElement();

  MOZ_ASSERT(aTarget == item.mEventTarget);

  // Disconnect the event target that will be popped.
  item.mEventTarget->Disconnect(lock);

  EventQueue* prevQueue =
      mNestedQueues.Length() == 1
          ? mBaseQueue.get()
          : mNestedQueues[mNestedQueues.Length() - 2].mQueue.get();

  // Move events from the old queue to the new one.
  nsCOMPtr<nsIRunnable> event;
  TimeDuration delay;
  while ((event = item.mQueue->GetEvent(lock, &delay))) {
    // preserve the event delay so far
    prevQueue->PutEvent(event.forget(), EventQueuePriority::Normal, lock,
                        &delay);
  }

  mNestedQueues.RemoveLastElement();
}

size_t ThreadEventQueue::SizeOfExcludingThis(
    mozilla::MallocSizeOf aMallocSizeOf) {
  size_t n = 0;

  {
    MutexAutoLock lock(mLock);
    n += mBaseQueue->SizeOfIncludingThis(aMallocSizeOf);
    n += mNestedQueues.ShallowSizeOfExcludingThis(aMallocSizeOf);
    for (auto& queue : mNestedQueues) {
      n += queue.mEventTarget->SizeOfIncludingThis(aMallocSizeOf);
    }
  }

  return SynchronizedEventQueue::SizeOfExcludingThis(aMallocSizeOf) + n;
}

already_AddRefed<nsIThreadObserver> ThreadEventQueue::GetObserver() {
  MutexAutoLock lock(mLock);
  return do_AddRef(mObserver);
}

already_AddRefed<nsIThreadObserver> ThreadEventQueue::GetObserverOnThread()
    MOZ_NO_THREAD_SAFETY_ANALYSIS {
  // only written on this thread
  return do_AddRef(mObserver);
}

void ThreadEventQueue::SetObserver(nsIThreadObserver* aObserver) {
  // Always called from the thread - single writer.
  nsCOMPtr<nsIThreadObserver> observer = aObserver;
  {
    MutexAutoLock lock(mLock);
    mObserver.swap(observer);
  }
  if (NS_IsMainThread()) {
    TaskController::Get()->SetThreadObserver(aObserver);
  }
}

nsresult ThreadEventQueue::RegisterShutdownTask(nsITargetShutdownTask* aTask) {
  NS_ENSURE_ARG(aTask);
  MutexAutoLock lock(mLock);
  if (mEventsAreDoomed || mShutdownTasksRun) {
    return NS_ERROR_UNEXPECTED;
  }
  MOZ_ASSERT(!mShutdownTasks.Contains(aTask));
  mShutdownTasks.AppendElement(aTask);
  return NS_OK;
}

nsresult ThreadEventQueue::UnregisterShutdownTask(
    nsITargetShutdownTask* aTask) {
  NS_ENSURE_ARG(aTask);
  MutexAutoLock lock(mLock);
  if (mEventsAreDoomed || mShutdownTasksRun) {
    return NS_ERROR_UNEXPECTED;
  }
  return mShutdownTasks.RemoveElement(aTask) ? NS_OK : NS_ERROR_UNEXPECTED;
}

void ThreadEventQueue::RunShutdownTasks() {
  nsTArray<nsCOMPtr<nsITargetShutdownTask>> shutdownTasks;
  {
    MutexAutoLock lock(mLock);
    shutdownTasks = std::move(mShutdownTasks);
    mShutdownTasks.Clear();
    mShutdownTasksRun = true;
  }
  for (auto& task : shutdownTasks) {
    task->TargetShutdown();
  }
}

ThreadEventQueue::NestedQueueItem::NestedQueueItem(
    UniquePtr<EventQueue> aQueue, ThreadEventTarget* aEventTarget)
    : mQueue(std::move(aQueue)), mEventTarget(aEventTarget) {}

98%


¤ Dauer der Verarbeitung: 0.2 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.