summaryrefslogtreecommitdiffstats
path: root/gfx/layers/ipc/CanvasThread.cpp
blob: 3d0a387c04bae78164f14a410c7f52ad173bd256 (plain)
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
/* -*- 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 "CanvasThread.h"

#include "mozilla/SharedThreadPool.h"
#include "nsThreadUtils.h"
#include "prsystem.h"

bool NS_IsInCanvasThreadOrWorker() {
  return mozilla::layers::CanvasThreadHolder::IsInCanvasThreadOrWorker();
}

namespace mozilla {
namespace layers {

StaticDataMutex<StaticRefPtr<CanvasThreadHolder>>
    CanvasThreadHolder::sCanvasThreadHolder("sCanvasThreadHolder");

CanvasThreadHolder::CanvasThreadHolder(
    already_AddRefed<nsISerialEventTarget> aCanvasThread,
    already_AddRefed<nsIThreadPool> aCanvasWorkers)
    : mCanvasThread(aCanvasThread),
      mCanvasWorkers(aCanvasWorkers),
      mCompositorThreadKeepAlive(CompositorThreadHolder::GetSingleton()) {
  MOZ_ASSERT(NS_IsInCompositorThread());
  MOZ_ASSERT(mCanvasThread);
  MOZ_ASSERT(mCanvasWorkers);
}

CanvasThreadHolder::~CanvasThreadHolder() {
  // Note we can't just use NS_IsInCompositorThread() here because
  // sCompositorThreadHolder might have already gone.
  MOZ_ASSERT(
      mCompositorThreadKeepAlive->GetCompositorThread()->IsOnCurrentThread());
}

/* static */
already_AddRefed<CanvasThreadHolder> CanvasThreadHolder::EnsureCanvasThread() {
  MOZ_ASSERT(NS_IsInCompositorThread());

  auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock();
  if (!lockedCanvasThreadHolder.ref()) {
    nsCOMPtr<nsISerialEventTarget> canvasThread;
    nsresult rv =
        NS_CreateBackgroundTaskQueue("Canvas", getter_AddRefs(canvasThread));
    NS_ENSURE_SUCCESS(rv, nullptr);

    // Given that the canvas workers are receiving instructions from
    // content processes, it probably doesn't make sense to have more than
    // half the number of processors doing canvas drawing. We set the
    // lower limit to 2, so that even on single processor systems, if
    // there is more than one window with canvas drawing, the OS can
    // manage the load between them.
    uint32_t threadLimit = std::max(2, PR_GetNumberOfProcessors() / 2);
    nsCOMPtr<nsIThreadPool> canvasWorkers =
        SharedThreadPool::Get("CanvasWorkers"_ns, threadLimit);
    if (!canvasWorkers) {
      return nullptr;
    }

    lockedCanvasThreadHolder.ref() =
        new CanvasThreadHolder(canvasThread.forget(), canvasWorkers.forget());
  }

  return do_AddRef(lockedCanvasThreadHolder.ref());
}

/* static */
void CanvasThreadHolder::ReleaseOnCompositorThread(
    already_AddRefed<CanvasThreadHolder> aCanvasThreadHolder) {
  RefPtr<CanvasThreadHolder> canvasThreadHolder = aCanvasThreadHolder;
  auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock();
  lockedCanvasThreadHolder.ref()
      ->mCompositorThreadKeepAlive->GetCompositorThread()
      ->Dispatch(NS_NewRunnableFunction(
          "CanvasThreadHolder::StaticRelease",
          [canvasThreadHolder = std::move(canvasThreadHolder)]() mutable {
            RefPtr<CanvasThreadHolder> threadHolder =
                canvasThreadHolder.forget();
            threadHolder = nullptr;

            auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock();
            if (lockedCanvasThreadHolder.ref()->mRefCnt == 1) {
              lockedCanvasThreadHolder.ref() = nullptr;
            }
          }));
}

/* static */
bool CanvasThreadHolder::IsInCanvasThread() {
  auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock();
  return lockedCanvasThreadHolder.ref() &&
         lockedCanvasThreadHolder.ref()->mCanvasThread->IsOnCurrentThread();
}

/* static */
bool CanvasThreadHolder::IsInCanvasWorker() {
  auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock();
  return lockedCanvasThreadHolder.ref() &&
         lockedCanvasThreadHolder.ref()->mCanvasWorkers->IsOnCurrentThread();
}

/* static */
bool CanvasThreadHolder::IsInCanvasThreadOrWorker() {
  auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock();
  return lockedCanvasThreadHolder.ref() &&
         (lockedCanvasThreadHolder.ref()->mCanvasWorkers->IsOnCurrentThread() ||
          lockedCanvasThreadHolder.ref()->mCanvasThread->IsOnCurrentThread());
}

/* static */
void CanvasThreadHolder::MaybeDispatchToCanvasThread(
    already_AddRefed<nsIRunnable> aRunnable) {
  auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock();
  if (!lockedCanvasThreadHolder.ref()) {
    // There is no canvas thread just release the runnable.
    nsCOMPtr<nsIRunnable> runnable = aRunnable;
    return;
  }

  lockedCanvasThreadHolder.ref()->mCanvasThread->Dispatch(std::move(aRunnable));
}

void CanvasThreadHolder::DispatchToCanvasThread(
    already_AddRefed<nsIRunnable> aRunnable) {
  mCanvasThread->Dispatch(std::move(aRunnable));
}

already_AddRefed<TaskQueue> CanvasThreadHolder::CreateWorkerTaskQueue() {
  return TaskQueue::Create(do_AddRef(mCanvasWorkers), "CanvasWorker").forget();
}

}  // namespace layers
}  // namespace mozilla