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.

Header

Mercurial (c68fe15a81fc)

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
/* -*- 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/SchedulerGroup.h"

#include <utility>

#include "jsfriendapi.h"
#include "mozilla/Atomics.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/ScriptSettings.h"
#include "nsINamed.h"
#include "nsQueryObject.h"
#include "nsThreadUtils.h"

using namespace mozilla;

namespace {

static Atomic<uint64_t> gEarliestUnprocessedVsync(0);

}  // namespace

/* static */
nsresult SchedulerGroup::UnlabeledDispatch(
    TaskCategory aCategory, already_AddRefed<nsIRunnable>&& aRunnable) {
  if (NS_IsMainThread()) {
    return NS_DispatchToCurrentThread(std::move(aRunnable));
  } else {
    return NS_DispatchToMainThread(std::move(aRunnable));
  }
}

/* static */
void SchedulerGroup::MarkVsyncReceived() {
  if (gEarliestUnprocessedVsync) {
    // If we've seen a vsync already, but haven't handled it, keep the
    // older one.
    return;
  }

  MOZ_ASSERT(!NS_IsMainThread());
  bool inconsistent = false;
  TimeStamp creation = TimeStamp::ProcessCreation(&inconsistent);
  if (inconsistent) {
    return;
  }

  gEarliestUnprocessedVsync = (TimeStamp::Now() - creation).ToMicroseconds();
}

/* static */
void SchedulerGroup::MarkVsyncRan() { gEarliestUnprocessedVsync = 0; }

SchedulerGroup::SchedulerGroup() : mIsRunning(false) {}

/* static */
nsresult SchedulerGroup::DispatchWithDocGroup(
    TaskCategory aCategory, already_AddRefed<nsIRunnable>&& aRunnable,
    dom::DocGroup* aDocGroup) {
  return LabeledDispatch(aCategory, std::move(aRunnable), aDocGroup);
}

/* static */
nsresult SchedulerGroup::Dispatch(TaskCategory aCategory,
                                  already_AddRefed<nsIRunnable>&& aRunnable) {
  return LabeledDispatch(aCategory, std::move(aRunnable), nullptr);
}

/* static */
nsresult SchedulerGroup::LabeledDispatch(
    TaskCategory aCategory, already_AddRefed<nsIRunnable>&& aRunnable,
    dom::DocGroup* aDocGroup) {
  nsCOMPtr<nsIRunnable> runnable(aRunnable);
  if (XRE_IsContentProcess()) {
    RefPtr<Runnable> internalRunnable =
        new Runnable(runnable.forget(), aDocGroup);
    return InternalUnlabeledDispatch(aCategory, internalRunnable.forget());
  }
  return UnlabeledDispatch(aCategory, runnable.forget());
}

/*static*/
nsresult SchedulerGroup::InternalUnlabeledDispatch(
    TaskCategory aCategory, already_AddRefed<Runnable>&& aRunnable) {
  if (NS_IsMainThread()) {
    // NS_DispatchToCurrentThread will not leak the passed in runnable
    // when it fails, so we don't need to do anything special.
    return NS_DispatchToCurrentThread(std::move(aRunnable));
  }

  RefPtr<Runnable> runnable(aRunnable);
  nsresult rv = NS_DispatchToMainThread(do_AddRef(runnable));
  if (NS_FAILED(rv)) {
    // Dispatch failed.  This is a situation where we would have used
    // NS_DispatchToMainThread rather than calling into the SchedulerGroup
    // machinery, and the caller would be expecting to leak the nsIRunnable
    // originally passed in.  But because we've had to wrap things up
    // internally, we were going to leak the nsIRunnable *and* our Runnable
    // wrapper.  But there's no reason that we have to leak our Runnable
    // wrapper; we can just leak the wrapped nsIRunnable, and let the caller
    // take care of unleaking it if they need to.
    Unused << runnable->mRunnable.forget().take();
    nsrefcnt refcnt = runnable.get()->Release();
    MOZ_RELEASE_ASSERT(refcnt == 1, "still holding an unexpected reference!");
  }

  return rv;
}

SchedulerGroup::Runnable::Runnable(already_AddRefed<nsIRunnable>&& aRunnable,
                                   dom::DocGroup* aDocGroup)
    : mozilla::Runnable("SchedulerGroup::Runnable"),
      mRunnable(std::move(aRunnable)),
      mDocGroup(aDocGroup) {}

dom::DocGroup* SchedulerGroup::Runnable::DocGroup() const { return mDocGroup; }

#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
NS_IMETHODIMP
SchedulerGroup::Runnable::GetName(nsACString& aName) {
  // Try to get a name from the underlying runnable.
  nsCOMPtr<nsINamed> named = do_QueryInterface(mRunnable);
  if (named) {
    named->GetName(aName);
  }
  if (aName.IsEmpty()) {
    aName.AssignLiteral("anonymous");
  }

  return NS_OK;
}
#endif

NS_IMETHODIMP
SchedulerGroup::Runnable::Run() {
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
  // The runnable's destructor can have side effects, so try to execute it in
  // the scope of the SchedulerGroup.
  nsCOMPtr<nsIRunnable> runnable(std::move(mRunnable));
  return runnable->Run();
}

NS_IMETHODIMP
SchedulerGroup::Runnable::GetPriority(uint32_t* aPriority) {
  *aPriority = nsIRunnablePriority::PRIORITY_NORMAL;
  nsCOMPtr<nsIRunnablePriority> runnablePrio = do_QueryInterface(mRunnable);
  return runnablePrio ? runnablePrio->GetPriority(aPriority) : NS_OK;
}

NS_IMPL_ISUPPORTS_INHERITED(SchedulerGroup::Runnable, mozilla::Runnable,
                            nsIRunnablePriority, SchedulerGroup::Runnable)