Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/vr/VRThread.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "VRThread.h"
8
#include "nsThreadUtils.h"
9
10
namespace mozilla {
11
12
namespace gfx {
13
14
static StaticRefPtr<VRListenerThreadHolder> sVRListenerThreadHolder;
15
static bool sFinishedVRListenerShutDown = false;
16
static const uint32_t kDefaultThreadLifeTime = 60; // in 60 seconds.
17
static const uint32_t kDelayPostTaskTime = 20000; // in 20000 ms.
18
19
VRListenerThreadHolder* GetVRListenerThreadHolder()
20
0
{
21
0
  return sVRListenerThreadHolder;
22
0
}
23
24
base::Thread*
25
VRListenerThread()
26
0
{
27
0
  return sVRListenerThreadHolder
28
0
         ? sVRListenerThreadHolder->GetThread()
29
0
         : nullptr;
30
0
}
31
32
/* static */ MessageLoop*
33
VRListenerThreadHolder::Loop()
34
0
{
35
0
  return VRListenerThread() ? VRListenerThread()->message_loop() : nullptr;
36
0
}
37
38
VRListenerThreadHolder*
39
VRListenerThreadHolder::GetSingleton()
40
0
{
41
0
  return sVRListenerThreadHolder;
42
0
}
43
44
VRListenerThreadHolder::VRListenerThreadHolder()
45
 : mThread(CreateThread())
46
0
{
47
0
  MOZ_ASSERT(NS_IsMainThread());
48
0
}
49
50
VRListenerThreadHolder::~VRListenerThreadHolder()
51
0
{
52
0
  MOZ_ASSERT(NS_IsMainThread());
53
0
  DestroyThread(mThread);
54
0
}
55
56
/* static */ void
57
VRListenerThreadHolder::DestroyThread(base::Thread* aThread)
58
0
{
59
0
  MOZ_ASSERT(NS_IsMainThread());
60
0
  MOZ_ASSERT(!sVRListenerThreadHolder,
61
0
             "We shouldn't be destroying the VR listener thread yet.");
62
0
  delete aThread;
63
0
  sFinishedVRListenerShutDown = true;
64
0
}
65
66
/* static */ base::Thread*
67
VRListenerThreadHolder::CreateThread()
68
0
{
69
0
  MOZ_ASSERT(NS_IsMainThread());
70
0
  MOZ_ASSERT(!sVRListenerThreadHolder, "The VR listener thread has already been started!");
71
0
72
0
  base::Thread* vrThread = new base::Thread("VRListener");
73
0
  base::Thread::Options options;
74
0
  /* Timeout values are powers-of-two to enable us get better data.
75
0
     128ms is chosen for transient hangs because 8Hz should be the minimally
76
0
     acceptable goal for Compositor responsiveness (normal goal is 60Hz). */
77
0
  options.transient_hang_timeout = 128; // milliseconds
78
0
  /* 2048ms is chosen for permanent hangs because it's longer than most
79
0
   * Compositor hangs seen in the wild, but is short enough to not miss getting
80
0
   * native hang stacks. */
81
0
  options.permanent_hang_timeout = 2048; // milliseconds
82
0
83
0
  if (!vrThread->StartWithOptions(options)) {
84
0
    delete vrThread;
85
0
    return nullptr;
86
0
  }
87
0
88
0
  return vrThread;
89
0
}
90
91
void
92
VRListenerThreadHolder::Start()
93
0
{
94
0
  MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!");
95
0
  MOZ_ASSERT(!sVRListenerThreadHolder, "The VR listener thread has already been started!");
96
0
97
0
  sVRListenerThreadHolder = new VRListenerThreadHolder();
98
0
}
99
100
void
101
VRListenerThreadHolder::Shutdown()
102
0
{
103
0
  MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!");
104
0
  MOZ_ASSERT(sVRListenerThreadHolder, "The VR listener thread has already been shut down!");
105
0
106
0
  sVRListenerThreadHolder = nullptr;
107
0
108
0
  SpinEventLoopUntil([&]() { return sFinishedVRListenerShutDown; });
109
0
}
110
111
/* static */ bool
112
VRListenerThreadHolder::IsInVRListenerThread()
113
0
{
114
0
  return VRListenerThread() &&
115
0
     VRListenerThread()->thread_id() == PlatformThread::CurrentId();
116
0
}
117
118
VRThread::VRThread(const nsCString& aName)
119
 : mThread(nullptr)
120
 , mLifeTime(kDefaultThreadLifeTime)
121
 , mStarted(false)
122
0
{
123
0
  mName = aName;
124
0
}
125
126
VRThread::~VRThread()
127
0
{
128
0
  Shutdown();
129
0
}
130
131
void
132
VRThread::Start()
133
0
{
134
0
  MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread());
135
0
136
0
  if (!mThread) {
137
0
    nsresult rv = NS_NewNamedThread(mName, getter_AddRefs(mThread));
138
0
    MOZ_ASSERT(mThread);
139
0
140
0
    if (NS_FAILED(rv)) {
141
0
      MOZ_ASSERT(false, "Failed to create a vr thread.");
142
0
    }
143
0
    RefPtr<Runnable> runnable =
144
0
      NewRunnableMethod<TimeStamp>(
145
0
        "gfx::VRThread::CheckLife", this, &VRThread::CheckLife, TimeStamp::Now());
146
0
    // Post it to the main thread for tracking the lifetime.
147
0
    nsCOMPtr<nsIThread> mainThread;
148
0
    rv = NS_GetMainThread(getter_AddRefs(mainThread));
149
0
    if (NS_FAILED(rv)) {
150
0
      NS_WARNING("VRThread::Start() could not get Main thread");
151
0
      return;
152
0
    }
153
0
    mainThread->DelayedDispatch(runnable.forget(), kDelayPostTaskTime);
154
0
  }
155
0
  mStarted = true;
156
0
  mLastActiveTime = TimeStamp::Now();
157
0
}
158
159
void
160
VRThread::Shutdown()
161
0
{
162
0
  if (mThread) {
163
0
    mThread->Shutdown();
164
0
    mThread = nullptr;
165
0
  }
166
0
  mStarted = false;
167
0
}
168
169
const nsCOMPtr<nsIThread>
170
VRThread::GetThread() const
171
0
{
172
0
  return mThread;
173
0
}
174
175
void
176
VRThread::PostTask(already_AddRefed<Runnable> aTask)
177
0
{
178
0
  PostDelayedTask(std::move(aTask), 0);
179
0
}
180
181
void
182
VRThread::PostDelayedTask(already_AddRefed<Runnable> aTask,
183
                          uint32_t aTime)
184
0
{
185
0
  MOZ_ASSERT(mStarted, "Must call Start() before posting tasks.");
186
0
  MOZ_ASSERT(mThread);
187
0
  mLastActiveTime = TimeStamp::Now();
188
0
189
0
  if (!aTime) {
190
0
    mThread->Dispatch(std::move(aTask), NS_DISPATCH_NORMAL);
191
0
  } else {
192
0
    mThread->DelayedDispatch(std::move(aTask), aTime);
193
0
  }
194
0
}
195
196
void
197
VRThread::CheckLife(TimeStamp aCheckTimestamp)
198
0
{
199
0
  // VR system is going to shutdown.
200
0
  if (!mStarted) {
201
0
    Shutdown();
202
0
    return;
203
0
  }
204
0
205
0
  const TimeDuration timeout = TimeDuration::FromSeconds(mLifeTime);
206
0
  if ((aCheckTimestamp - mLastActiveTime) > timeout) {
207
0
    Shutdown();
208
0
  } else {
209
0
    RefPtr<Runnable> runnable =
210
0
      NewRunnableMethod<TimeStamp>(
211
0
        "gfx::VRThread::CheckLife", this, &VRThread::CheckLife, TimeStamp::Now());
212
0
    // Post it to the main thread for tracking the lifetime.
213
0
    nsCOMPtr<nsIThread> mainThread;
214
0
    nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
215
0
    if (NS_FAILED(rv)) {
216
0
      NS_WARNING("VRThread::CheckLife() could not get Main thread");
217
0
      return;
218
0
    }
219
0
    mainThread->DelayedDispatch(runnable.forget(), kDelayPostTaskTime);
220
0
  }
221
0
}
222
223
void
224
VRThread::SetLifeTime(uint32_t aLifeTime)
225
0
{
226
0
  mLifeTime = aLifeTime;
227
0
}
228
229
uint32_t
230
VRThread::GetLifeTime()
231
0
{
232
0
  return mLifeTime;
233
0
}
234
235
bool
236
VRThread::IsActive()
237
0
{
238
0
  return !!mThread;
239
0
}
240
241
} // namespace gfx
242
} // namespace mozilla