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 https://mozilla.org/MPL/2.0/. */
6 
7 #include "GraphRunner.h"
8 
9 #include "GraphDriver.h"
10 #include "MediaTrackGraph.h"
11 #include "MediaTrackGraphImpl.h"
12 #include "nsISupportsImpl.h"
13 #include "nsISupportsPriority.h"
14 #include "prthread.h"
15 #include "Tracing.h"
16 #include "audio_thread_priority.h"
17 
18 namespace mozilla {
19 
GraphRunner(MediaTrackGraphImpl * aGraph,already_AddRefed<nsIThread> aThread)20 GraphRunner::GraphRunner(MediaTrackGraphImpl* aGraph,
21                          already_AddRefed<nsIThread> aThread)
22     : Runnable("GraphRunner"),
23       mMonitor("GraphRunner::mMonitor"),
24       mGraph(aGraph),
25       mThreadState(ThreadState::Wait),
26       mThread(aThread) {
27   mThread->Dispatch(do_AddRef(this));
28 }
29 
~GraphRunner()30 GraphRunner::~GraphRunner() {
31   MOZ_ASSERT(mThreadState == ThreadState::Shutdown);
32 }
33 
34 /* static */
Create(MediaTrackGraphImpl * aGraph)35 already_AddRefed<GraphRunner> GraphRunner::Create(MediaTrackGraphImpl* aGraph) {
36   nsCOMPtr<nsIThread> thread;
37   if (NS_WARN_IF(NS_FAILED(
38           NS_NewNamedThread("GraphRunner", getter_AddRefs(thread))))) {
39     return nullptr;
40   }
41   nsCOMPtr<nsISupportsPriority> supportsPriority = do_QueryInterface(thread);
42   MOZ_ASSERT(supportsPriority);
43   MOZ_ALWAYS_SUCCEEDS(
44       supportsPriority->SetPriority(nsISupportsPriority::PRIORITY_HIGHEST));
45 
46   return do_AddRef(new GraphRunner(aGraph, thread.forget()));
47 }
48 
Shutdown()49 void GraphRunner::Shutdown() {
50   {
51     MonitorAutoLock lock(mMonitor);
52     MOZ_ASSERT(mThreadState == ThreadState::Wait);
53     mThreadState = ThreadState::Shutdown;
54     mMonitor.Notify();
55   }
56   mThread->Shutdown();
57 }
58 
OneIteration(GraphTime aStateTime,GraphTime aIterationEnd,AudioMixer * aMixer)59 auto GraphRunner::OneIteration(GraphTime aStateTime, GraphTime aIterationEnd,
60                                AudioMixer* aMixer) -> IterationResult {
61   TRACE();
62 
63   MonitorAutoLock lock(mMonitor);
64   MOZ_ASSERT(mThreadState == ThreadState::Wait);
65   mIterationState = Some(IterationState(aStateTime, aIterationEnd, aMixer));
66 
67 #ifdef DEBUG
68   if (auto audioDriver = mGraph->CurrentDriver()->AsAudioCallbackDriver()) {
69     mAudioDriverThreadId = audioDriver->ThreadId();
70   } else if (auto clockDriver =
71                  mGraph->CurrentDriver()->AsSystemClockDriver()) {
72     mClockDriverThread = clockDriver->Thread();
73   } else {
74     MOZ_CRASH("Unknown GraphDriver");
75   }
76 #endif
77   // Signal that mIterationState was updated
78   mThreadState = ThreadState::Run;
79   mMonitor.Notify();
80   // Wait for mIterationResult to update
81   do {
82     mMonitor.Wait();
83   } while (mThreadState == ThreadState::Run);
84 
85 #ifdef DEBUG
86   mAudioDriverThreadId = std::thread::id();
87   mClockDriverThread = nullptr;
88 #endif
89 
90   mIterationState = Nothing();
91 
92   IterationResult result = std::move(mIterationResult);
93   mIterationResult = IterationResult();
94   return result;
95 }
96 
Run()97 NS_IMETHODIMP GraphRunner::Run() {
98 #ifndef XP_LINUX
99   atp_handle* handle =
100       atp_promote_current_thread_to_real_time(0, mGraph->GraphRate());
101 #endif
102 
103   nsCOMPtr<nsIThreadInternal> threadInternal = do_QueryInterface(mThread);
104   threadInternal->SetObserver(mGraph);
105 
106   MonitorAutoLock lock(mMonitor);
107   while (true) {
108     while (mThreadState == ThreadState::Wait) {
109       mMonitor.Wait();  // Wait for mIterationState to update or for shutdown
110     }
111     if (mThreadState == ThreadState::Shutdown) {
112       break;
113     }
114     MOZ_DIAGNOSTIC_ASSERT(mIterationState.isSome());
115     TRACE();
116     mIterationResult = mGraph->OneIterationImpl(mIterationState->StateTime(),
117                                                 mIterationState->IterationEnd(),
118                                                 mIterationState->Mixer());
119     // Signal that mIterationResult was updated
120     mThreadState = ThreadState::Wait;
121     mMonitor.Notify();
122   }
123 
124 #ifndef XP_LINUX
125   if (handle) {
126     atp_demote_current_thread_from_real_time(handle);
127   }
128 #endif
129 
130   return NS_OK;
131 }
132 
OnThread() const133 bool GraphRunner::OnThread() const {
134   return mThread->EventTarget()->IsOnCurrentThread();
135 }
136 
137 #ifdef DEBUG
InDriverIteration(const GraphDriver * aDriver) const138 bool GraphRunner::InDriverIteration(const GraphDriver* aDriver) const {
139   if (!OnThread()) {
140     return false;
141   }
142 
143   if (auto audioDriver = aDriver->AsAudioCallbackDriver()) {
144     return audioDriver->ThreadId() == mAudioDriverThreadId;
145   }
146 
147   if (auto clockDriver = aDriver->AsSystemClockDriver()) {
148     return clockDriver->Thread() == mClockDriverThread;
149   }
150 
151   MOZ_CRASH("Unknown driver");
152 }
153 #endif
154 
155 }  // namespace mozilla
156