Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  TaskQueue.h

  Sprache: 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/. */


#ifndef TaskQueue_h_
#define TaskQueue_h_

#include "mozilla/AbstractThread.h"
#include "mozilla/Maybe.h"
#include "mozilla/Monitor.h"
#include "mozilla/MozPromise.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TargetShutdownTaskSet.h"
#include "mozilla/TaskDispatcher.h"
#include "nsIDirectTaskDispatcher.h"
#include "nsITargetShutdownTask.h"
#include "nsThreadUtils.h"

#define MOZILLA_TASKQUEUE_IID \
  {0xb5181e3a, 0x39cf, 0x4d32, {0x81, 0x4a, 0xea, 0x86, 0x94, 0x16, 0x95, 0xd1}}

namespace mozilla {

typedef MozPromise<boolboolfalse> ShutdownPromise;

class TaskQueueTargetShutdownTask;

// Abstracts executing runnables in order on an arbitrary event target. The
// runnables dispatched to the TaskQueue will be executed in the order in which
// they're received, and are guaranteed to not be executed concurrently.
// They may be executed on different threads, and a memory barrier is used
// to make this threadsafe for objects that aren't already threadsafe.
//
// Note that since a TaskQueue is an AbstractThread and thus an nsIEventTarget,
// it's possible to construct a hierarchy of TaskQueues.
// Consider these three TaskQueues:
//
//  TQ1 dispatches to the main thread
//  TQ2 dispatches to TQ1
//  TQ3 dispatches to TQ1
//
// This ensures there is only ever a single runnable from the entire chain on
// the main thread.  It also ensures that TQ2 and TQ3 only have a single
// runnable in TQ1 at any time.
//
// This arrangement lets you prioritize work by dispatching runnables directly
// to TQ1.  You can issue many runnables for important work.  Meanwhile the TQ2
// and TQ3 work will always execute at most one runnable and then yield.
//
// A TaskQueue does not require explicit shutdown unless it has shutdown tasks
// registered to it. TaskQueue provides a BeginShutdown() method that places
// TaskQueue in a shut down state and returns a promise that gets resolved once
// all shutdown and pending tasks have completed.
//
// On implicit release without regular shutdown the runner will have ensured
// that all dispatched tasks completed before (provided the target
// SUPPORTS_SHUTDOWN_TASK_DISPATCH, as most do) and TaskQueue asserts there to
// be no shutdown tasks registered. If there are shutdown tasks registered,
// TaskQueue registers a shutdown task with its target to guarantee an explicit
// shutdown when the target goes away.
class TaskQueue final : public AbstractThread,
                        public nsIDirectTaskDispatcher,
                        public nsITargetShutdownTask {
  class EventTargetWrapper;

 public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSIDIRECTTASKDISPATCHER
  MOZ_DECLARE_REFCOUNTED_TYPENAME(TaskQueue)
  NS_INLINE_DECL_STATIC_IID(MOZILLA_TASKQUEUE_IID)

  void TargetShutdown() override;

  static RefPtr<TaskQueue> Create(already_AddRefed<nsIEventTarget> aTarget,
                                  StaticString aName,
                                  bool aSupportsTailDispatch = false);

  TaskDispatcher& TailDispatcher() override;

  NS_IMETHOD DispatchFromScript(nsIRunnable* aEvent,
                                DispatchFlags aFlags) override {
    return Dispatch(do_AddRef(aEvent), aFlags);
  }

  NS_IMETHOD Dispatch(already_AddRefed<nsIRunnable> aEvent,
                      DispatchFlags aFlags) override {
    // NOTE: This dispatch implementation never leaks the runnable on failure,
    // even if `NS_DISPATCH_FALLIBLE` is not specified.
    nsCOMPtr<nsIRunnable> runnable = aEvent;
    {
      MonitorAutoLock mon(mQueueMonitor);
      return DispatchLocked(/* passed by ref */ runnable, aFlags,
                            NormalDispatch);
    }
    // If the ownership of |r| is not transferred in DispatchLocked() due to
    // dispatch failure, it will be deleted here outside the lock. We do so
    // since the destructor of the runnable might access TaskQueue and result
    // in deadlocks.
  }

  [[nodiscard]] nsresult Dispatch(
      already_AddRefed<nsIRunnable> aRunnable,
      DispatchReason aReason = NormalDispatch) override {
    nsCOMPtr<nsIRunnable> r = aRunnable;
    {
      MonitorAutoLock mon(mQueueMonitor);
      return DispatchLocked(/* passed by ref */ r, NS_DISPATCH_NORMAL, aReason);
    }
    // If the ownership of |r| is not transferred in DispatchLocked() due to
    // dispatch failure, it will be deleted here outside the lock. We do so
    // since the destructor of the runnable might access TaskQueue and result
    // in deadlocks.
  }

  // So we can access nsIEventTarget::Dispatch(nsIRunnable*, uint32_t aFlags)
  using nsIEventTarget::Dispatch;

  NS_IMETHOD RegisterShutdownTask(nsITargetShutdownTask* aTask) override;
  NS_IMETHOD UnregisterShutdownTask(nsITargetShutdownTask* aTask) override;
  NS_IMETHOD_(FeatureFlags) GetFeatures() override;

  using CancelPromise = MozPromise<boolboolfalse>;

  // Puts the queue in a shutdown state and returns immediately. The queue will
  // remain alive at least until all the events are drained, because the Runners
  // hold a strong reference to the task queue, and one of them is always held
  // by the target event queue when the task queue is non-empty.
  //
  // The returned promise is resolved when the queue goes empty.
  RefPtr<ShutdownPromise> BeginShutdown();

  // Blocks until all task finish executing.
  void AwaitIdle();

  // Blocks until the queue is flagged for shutdown and all tasks have finished
  // executing.
  void AwaitShutdownAndIdle();

  bool IsEmpty();

  // Returns true if the current thread is currently running a Runnable in
  // the task queue.
  bool IsCurrentThreadIn() const override;
  using nsISerialEventTarget::IsOnCurrentThread;

  class Observer {
   public:
    NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
    // Called before an event is processed on the TaskQueue on its event target.
    virtual void WillProcessEvent(TaskQueue* aQueue) = 0;
    // Called after an event has been processed on the TaskQueue on its event
    // target.
    // Note that it is not safe to add direct tasks from DidProcessEvent().
    virtual void DidProcessEvent(TaskQueue* aQueue) = 0;

   protected:
    virtual ~Observer() = default;
  };

  // Set an observer to be notified as this TaskQueue processes events.
  // Callable from any thread. Transactional, i.e. WillProcess always comes
  // first and is always matched by DidProcess.
  void SetObserver(Observer* aObserver);

 private:
  TaskQueue(already_AddRefed<nsIEventTarget> aTarget, const char* aName,
            bool aSupportsTailDispatch);

  virtual ~TaskQueue();

  // Blocks until all task finish executing. Called internally by methods
  // that need to wait until the task queue is idle.
  // mQueueMonitor must be held.
  void AwaitIdleLocked();

  nsresult DispatchLocked(nsCOMPtr<nsIRunnable>& aRunnable,
                          DispatchFlags aFlags,
                          DispatchReason aReason = NormalDispatch);

  void MaybeResolveShutdown();

  void MaybeUnregisterTargetShutdownTask() MOZ_REQUIRES(mQueueMonitor);

  nsCOMPtr<nsIEventTarget> mTarget MOZ_GUARDED_BY(mQueueMonitor);

  // Monitor that protects the queue, mIsRunning, mIsShutdown and
  // mShutdownTasks;
  Monitor mQueueMonitor;

  struct TaskStruct {
    nsCOMPtr<nsIRunnable> event;
    DispatchFlags flags;
  };
  // NOTE: A `mozilla::Queue` is not used here, as Runner steals tasks from the
  // TaskQueue in bulk. The efficiency of `mozilla::Queue` for mixing adding and
  // removing entries would be unused. A small inline buffer is used to reduce
  // allocations in the common case where few tasks are queued.
  using TaskArray = AutoTArray<TaskStruct, 4>;

  // Queue of tasks to run.
  TaskArray mTasks MOZ_GUARDED_BY(mQueueMonitor);

  // List of tasks to run during shutdown.
  TargetShutdownTaskSet mShutdownTasks MOZ_GUARDED_BY(mQueueMonitor);

  // The thread currently running the task queue. We store a reference
  // to this so that IsCurrentThreadIn() can tell if the current thread
  // is the thread currently running in the task queue.
  //
  // This may be read on any thread, but may only be written on mRunningThread.
  // The thread can't die while we're running in it, and we only use it for
  // pointer-comparison with the current thread anyway - so we make it atomic
  // and don't refcount it.
  Atomic<PRThread*> mRunningThread;

  // RAII class that gets instantiated for each dispatched task.
  class AutoTaskGuard {
   public:
    AutoTaskGuard(TaskQueue* aQueue, TaskQueue::Observer* aObserver)
        : mQueue(aQueue), mObserver(aObserver), mLastCurrentThread(nullptr) {
      // NB: We don't hold the lock to aQueue here. Don't do anything that
      // might require it.
      MOZ_ASSERT(!mQueue->mTailDispatcher);
      mTaskDispatcher.emplace(aQueue,
                              /* aIsTailDispatcher = */ true);
      mQueue->mTailDispatcher = mTaskDispatcher.ptr();

      mLastCurrentThread = sCurrentThreadTLS.get();
      sCurrentThreadTLS.set(aQueue);

      MOZ_ASSERT(mQueue->mRunningThread == nullptr);
      mQueue->mRunningThread = PR_GetCurrentThread();

      mEventTargetGuard.emplace(mQueue);

      if (mObserver) {
        mObserver->WillProcessEvent(mQueue);
      }
    }

    ~AutoTaskGuard() {
      mTaskDispatcher->DrainDirectTasks();

      if (mObserver) {
        mObserver->DidProcessEvent(mQueue);
        MOZ_ASSERT(!mTaskDispatcher->HaveDirectTasks(),
                   "TaskQueue::Observer instance in "
                   "DidProcessEvent(TaskQueue*) added direct tasks in error");
      }

      mTaskDispatcher.reset();
      mQueue->mTailDispatcher = nullptr;

      mEventTargetGuard = Nothing();

      MOZ_ASSERT(mQueue->mRunningThread == PR_GetCurrentThread());
      mQueue->mRunningThread = nullptr;

      sCurrentThreadTLS.set(mLastCurrentThread);
    }

   private:
    Maybe<AutoTaskDispatcher> mTaskDispatcher;
    Maybe<SerialEventTargetGuard> mEventTargetGuard;
    TaskQueue* mQueue;
    TaskQueue::Observer* mObserver;
    AbstractThread* mLastCurrentThread;
  };

  TaskDispatcher* mTailDispatcher;

  // True if we have registered a target shutdown task with mTarget.
  bool mIsTargetShutdownTaskRegistered MOZ_GUARDED_BY(mQueueMonitor);

  // True if we've dispatched an event to the target to execute events from
  // the queue.
  bool mIsRunning MOZ_GUARDED_BY(mQueueMonitor);

  // True if we've started our shutdown process.
  bool mIsShutdown MOZ_GUARDED_BY(mQueueMonitor);
  MozPromiseHolder<ShutdownPromise> mShutdownPromise
      MOZ_GUARDED_BY(mQueueMonitor);

  // The name of this TaskQueue. Useful when debugging dispatch failures.
  const charconst mName;

  SimpleTaskQueue mDirectTasks;

  RefPtr<Observer> mObserver MOZ_GUARDED_BY(mQueueMonitor);

  class Runner : public Runnable {
   public:
    Runner(TaskQueue* aQueue, nsIEventTarget* aTarget, Observer* aObserver,
           TaskArray&& aTasks)
        : Runnable("TaskQueue::Runner"),
          mQueue(aQueue),
          mTarget(aTarget),
          mObserver(aObserver),
          mTasks(std::move(aTasks)) {}
    NS_IMETHOD Run() override;

   private:
    RefPtr<TaskQueue> mQueue;

    // Local cache of mTarget and mObserver in the task runner, so that they can
    // be read without acquiring mQueue->mMonitor. Re-loaded from the TaskQueue
    // every time mTasks is exhausted.
    nsCOMPtr<nsIEventTarget> mTarget;
    RefPtr<Observer> mObserver;

    // List of tasks taken from the TaskQueue and being actively processed.
    TaskArray mTasks;

    // Index of the next task to process in mTasks. Once this reaches
    // mTasks.Length(), Runner will re-load mTasks from the task queue and
    // continue running if new tasks have been posted.
    size_t mNextTask = 0;
  };
};

}  // namespace mozilla

#endif  // TaskQueue_h_

Messung V0.5 in Prozent
C=90 H=91 G=90

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

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 und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002