DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (409f3966645a)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
/* -*- 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/. */

#ifndef mozilla_SchedulerGroup_h
#define mozilla_SchedulerGroup_h

#include "mozilla/AbstractEventQueue.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Queue.h"
#include "mozilla/TaskCategory.h"
#include "mozilla/ThreadLocal.h"
#include "mozilla/TimeStamp.h"
#include "nsCOMPtr.h"
#include "nsILabelableRunnable.h"
#include "nsISupportsImpl.h"
#include "nsThreadUtils.h"

class nsIEventTarget;
class nsIRunnable;
class nsISerialEventTarget;

namespace mozilla {
class AbstractThread;
namespace dom {
class DocGroup;
class TabGroup;
}

#define NS_SCHEDULERGROUPRUNNABLE_IID \
{ 0xd31b7420, 0x872b, 0x4cfb, \
  { 0xa9, 0xc6, 0xae, 0x4c, 0x0f, 0x06, 0x36, 0x74 } }

// The "main thread" in Gecko will soon be a set of cooperatively scheduled
// "fibers". Global state in Gecko will be partitioned into a series of "groups"
// (with roughly one group per tab). Runnables will be annotated with the set of
// groups that they touch. Two runnables may run concurrently on different
// fibers as long as they touch different groups.
//
// A SchedulerGroup is an abstract class to represent a "group". Essentially the
// only functionality offered by a SchedulerGroup is the ability to dispatch
// runnables to the group. TabGroup, DocGroup, and SystemGroup are the concrete
// implementations of SchedulerGroup.
class SchedulerGroup : public LinkedListElement<SchedulerGroup>
{
public:
  SchedulerGroup();

  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING

  // This method returns true if all members of the "group" are in a
  // "background" state.
  virtual bool IsBackground() const { return false; }

  // This function returns true if it's currently safe to run code associated
  // with this SchedulerGroup. It will return true either if we're inside an
  // unlabeled runnable or if we're inside a runnable labeled with this
  // SchedulerGroup.
  bool IsSafeToRun() const
  {
    return !sTlsValidatingAccess.get() || mIsRunning;
  }

  // This function returns true if it's currently safe to run unlabeled code
  // with no known SchedulerGroup. It will only return true if we're inside an
  // unlabeled runnable.
  static bool IsSafeToRunUnlabeled()
  {
    return !sTlsValidatingAccess.get();
  }

  // Ensure that it's valid to access the TabGroup at this time.
  void ValidateAccess() const
  {
    MOZ_ASSERT(IsSafeToRun());
  }

  enum EnqueueStatus
  {
    NewlyQueued,
    AlreadyQueued,
  };

  // Records that this SchedulerGroup had an event enqueued in some
  // queue. Returns whether the SchedulerGroup was already in a queue before
  // EnqueueEvent() was called.
  EnqueueStatus EnqueueEvent()
  {
    mEventCount++;
    return mEventCount == 1 ? NewlyQueued : AlreadyQueued;
  }

  enum DequeueStatus
  {
    StillQueued,
    NoLongerQueued,
  };

  // Records that this SchedulerGroup had an event dequeued from some
  // queue. Returns whether the SchedulerGroup is still in a queue after
  // DequeueEvent() returns.
  DequeueStatus DequeueEvent()
  {
    mEventCount--;
    return mEventCount == 0 ? NoLongerQueued : StillQueued;
  }

  class Runnable final : public mozilla::Runnable
                       , public nsIRunnablePriority
                       , public nsILabelableRunnable
  {
  public:
    Runnable(already_AddRefed<nsIRunnable>&& aRunnable,
             SchedulerGroup* aGroup,
             dom::DocGroup* aDocGroup);

    bool GetAffectedSchedulerGroups(SchedulerGroupSet& aGroups) override;

    SchedulerGroup* Group() const { return mGroup; }
    dom::DocGroup* DocGroup() const;

#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
    NS_IMETHOD GetName(nsACString& aName) override;
#endif

    bool IsBackground() const { return mGroup->IsBackground(); }

    NS_DECL_ISUPPORTS_INHERITED
    NS_DECL_NSIRUNNABLE
    NS_DECL_NSIRUNNABLEPRIORITY

    NS_DECLARE_STATIC_IID_ACCESSOR(NS_SCHEDULERGROUPRUNNABLE_IID);

 private:
    friend class SchedulerGroup;

    ~Runnable() = default;

    nsCOMPtr<nsIRunnable> mRunnable;
    RefPtr<SchedulerGroup> mGroup;
    RefPtr<dom::DocGroup> mDocGroup;
  };
  friend class Runnable;

  bool* GetValidAccessPtr() { return &mIsRunning; }

  virtual nsresult Dispatch(TaskCategory aCategory,
                            already_AddRefed<nsIRunnable>&& aRunnable);

  virtual nsISerialEventTarget* EventTargetFor(TaskCategory aCategory) const;

  // Must always be called on the main thread. The returned AbstractThread can
  // always be used off the main thread.
  AbstractThread* AbstractMainThreadFor(TaskCategory aCategory);

  // This method performs a safe cast. It returns null if |this| is not of the
  // requested type.
  virtual dom::TabGroup* AsTabGroup() { return nullptr; }

  static nsresult UnlabeledDispatch(TaskCategory aCategory,
                                    already_AddRefed<nsIRunnable>&& aRunnable);

  static void MarkVsyncReceived();

  static void MarkVsyncRan();

  void SetIsRunning(bool aIsRunning) { mIsRunning = aIsRunning; }
  bool IsRunning() const { return mIsRunning; }

  enum ValidationType {
    StartValidation,
    EndValidation,
  };
  static void SetValidatingAccess(ValidationType aType);

  struct EpochQueueEntry
  {
    nsCOMPtr<nsIRunnable> mRunnable;
    uintptr_t mEpochNumber;

    EpochQueueEntry(already_AddRefed<nsIRunnable> aRunnable, uintptr_t aEpoch)
      : mRunnable(aRunnable)
      , mEpochNumber(aEpoch)
    {
    }
  };

  using RunnableEpochQueue = Queue<EpochQueueEntry, 32>;

  RunnableEpochQueue& GetQueue(mozilla::EventPriority aPriority)
  {
    return mEventQueues[size_t(aPriority)];
  }

protected:
  nsresult DispatchWithDocGroup(TaskCategory aCategory,
                                already_AddRefed<nsIRunnable>&& aRunnable,
                                dom::DocGroup* aDocGroup);

  static nsresult InternalUnlabeledDispatch(TaskCategory aCategory,
                                            already_AddRefed<Runnable>&& aRunnable);

  // Implementations are guaranteed that this method is called on the main
  // thread.
  virtual AbstractThread* AbstractMainThreadForImpl(TaskCategory aCategory);

  // Helper method to create an event target specific to a particular TaskCategory.
  virtual already_AddRefed<nsISerialEventTarget>
  CreateEventTargetFor(TaskCategory aCategory);

  // Given an event target returned by |dispatcher->CreateEventTargetFor|, this
  // function returns |dispatcher|.
  static SchedulerGroup* FromEventTarget(nsIEventTarget* aEventTarget);

  nsresult LabeledDispatch(TaskCategory aCategory,
                           already_AddRefed<nsIRunnable>&& aRunnable,
                           dom::DocGroup* aDocGroup);

  void CreateEventTargets(bool aNeedValidation);

  // Shuts down this dispatcher. If aXPCOMShutdown is true, invalidates this
  // dispatcher.
  void Shutdown(bool aXPCOMShutdown);

  static MOZ_THREAD_LOCAL(bool) sTlsValidatingAccess;

  bool mIsRunning;

  // Number of events that are currently enqueued for this SchedulerGroup
  // (across all queues).
  size_t mEventCount = 0;

  nsCOMPtr<nsISerialEventTarget> mEventTargets[size_t(TaskCategory::Count)];
  RefPtr<AbstractThread> mAbstractThreads[size_t(TaskCategory::Count)];
  RunnableEpochQueue mEventQueues[size_t(mozilla::EventPriority::Count)];
};

NS_DEFINE_STATIC_IID_ACCESSOR(SchedulerGroup::Runnable, NS_SCHEDULERGROUPRUNNABLE_IID);

} // namespace mozilla

#endif // mozilla_SchedulerGroup_h