OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/midi/midi_manager.h" | 5 #include "media/midi/midi_manager.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/message_loop/message_loop.h" | 8 #include "base/message_loop/message_loop.h" |
9 #include "base/metrics/histogram_macros.h" | 9 #include "base/metrics/histogram_macros.h" |
10 #include "base/trace_event/trace_event.h" | 10 #include "base/trace_event/trace_event.h" |
(...skipping 16 matching lines...) Expand all Loading... |
27 SESSION_STARTED, | 27 SESSION_STARTED, |
28 SESSION_ENDED, | 28 SESSION_ENDED, |
29 INITIALIZED, | 29 INITIALIZED, |
30 INPUT_PORT_ADDED, | 30 INPUT_PORT_ADDED, |
31 OUTPUT_PORT_ADDED, | 31 OUTPUT_PORT_ADDED, |
32 | 32 |
33 // New items should be inserted here, and |MAX| should point the last item. | 33 // New items should be inserted here, and |MAX| should point the last item. |
34 MAX = INITIALIZED, | 34 MAX = INITIALIZED, |
35 }; | 35 }; |
36 | 36 |
| 37 // Used in StartSession. |
| 38 enum class Completion { |
| 39 COMPLETE_SYNCHRONOUSLY, |
| 40 INVOKE_INITIALIZATION, |
| 41 COMPLETE_ASYNCHRONOUSLY, |
| 42 }; |
| 43 |
37 void ReportUsage(Usage usage) { | 44 void ReportUsage(Usage usage) { |
38 UMA_HISTOGRAM_ENUMERATION("Media.Midi.Usage", | 45 UMA_HISTOGRAM_ENUMERATION("Media.Midi.Usage", |
39 static_cast<Sample>(usage), | 46 static_cast<Sample>(usage), |
40 static_cast<Sample>(Usage::MAX) + 1); | 47 static_cast<Sample>(Usage::MAX) + 1); |
41 } | 48 } |
42 | 49 |
43 } // namespace | 50 } // namespace |
44 | 51 |
45 MidiManager::MidiManager() | 52 MidiManager::MidiManager() |
46 : initialized_(false), result_(Result::NOT_INITIALIZED) { | 53 : initialized_(false), finalized_(false), result_(Result::NOT_INITIALIZED) { |
47 ReportUsage(Usage::CREATED); | 54 ReportUsage(Usage::CREATED); |
48 } | 55 } |
49 | 56 |
50 MidiManager::~MidiManager() { | 57 MidiManager::~MidiManager() { |
51 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown", | 58 // Make sure that Finalize() is called to clean up resources allocated on |
52 static_cast<Sample>(result_), | 59 // the Chrome_IOThread. |
53 static_cast<Sample>(Result::MAX) + 1); | 60 DCHECK(finalized_); |
54 } | 61 } |
55 | 62 |
56 #if !defined(OS_MACOSX) && !defined(OS_WIN) && \ | 63 #if !defined(OS_MACOSX) && !defined(OS_WIN) && \ |
57 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID) | 64 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID) |
58 MidiManager* MidiManager::Create() { | 65 MidiManager* MidiManager::Create() { |
59 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS); | 66 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS); |
60 return new MidiManager; | 67 return new MidiManager; |
61 } | 68 } |
62 #endif | 69 #endif |
63 | 70 |
| 71 void MidiManager::Shutdown() { |
| 72 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown", |
| 73 static_cast<int>(result_), |
| 74 static_cast<int>(Result::MAX) + 1); |
| 75 base::AutoLock auto_lock(lock_); |
| 76 if (session_thread_runner_) { |
| 77 session_thread_runner_->PostTask( |
| 78 FROM_HERE, base::Bind(&MidiManager::ShutdownOnSessionThread, |
| 79 base::Unretained(this))); |
| 80 session_thread_runner_ = nullptr; |
| 81 } else { |
| 82 finalized_ = true; |
| 83 } |
| 84 } |
| 85 |
64 void MidiManager::StartSession(MidiManagerClient* client) { | 86 void MidiManager::StartSession(MidiManagerClient* client) { |
65 ReportUsage(Usage::SESSION_STARTED); | 87 ReportUsage(Usage::SESSION_STARTED); |
66 | 88 |
67 bool session_is_ready; | 89 Completion completion = Completion::COMPLETE_SYNCHRONOUSLY; |
68 bool session_needs_initialization = false; | 90 Result result = Result::NOT_INITIALIZED; |
69 bool too_many_pending_clients_exist = false; | |
70 | 91 |
71 { | 92 { |
72 base::AutoLock auto_lock(lock_); | 93 base::AutoLock auto_lock(lock_); |
73 session_is_ready = initialized_; | |
74 if (clients_.find(client) != clients_.end() || | 94 if (clients_.find(client) != clients_.end() || |
75 pending_clients_.find(client) != pending_clients_.end()) { | 95 pending_clients_.find(client) != pending_clients_.end()) { |
76 // Should not happen. But just in case the renderer is compromised. | 96 // Should not happen. But just in case the renderer is compromised. |
77 NOTREACHED(); | 97 NOTREACHED(); |
78 return; | 98 return; |
79 } | 99 } |
80 if (!session_is_ready) { | 100 |
| 101 if (initialized_) { |
| 102 // Platform dependent initialization was already finished for previously |
| 103 // initialized clients. |
| 104 if (result_ == Result::OK) { |
| 105 AddInitialPorts(client); |
| 106 clients_.insert(client); |
| 107 } |
| 108 // Complete synchronously with |result_|; |
| 109 result = result_; |
| 110 } else { |
| 111 bool too_many_pending_clients_exist = |
| 112 pending_clients_.size() >= kMaxPendingClientCount; |
81 // Do not accept a new request if the pending client list contains too | 113 // Do not accept a new request if the pending client list contains too |
82 // many clients. | 114 // many clients, or Shutdown() was already called. |
83 too_many_pending_clients_exist = | 115 if (too_many_pending_clients_exist || finalized_) { |
84 pending_clients_.size() >= kMaxPendingClientCount; | 116 result = Result::INITIALIZATION_ERROR; |
85 | 117 } else { |
86 if (!too_many_pending_clients_exist) { | |
87 // Call StartInitialization() only for the first request. | 118 // Call StartInitialization() only for the first request. |
88 session_needs_initialization = pending_clients_.empty(); | 119 if (pending_clients_.empty()) { |
| 120 completion = Completion::INVOKE_INITIALIZATION; |
| 121 session_thread_runner_ = base::MessageLoop::current()->task_runner(); |
| 122 } else { |
| 123 completion = Completion::COMPLETE_ASYNCHRONOUSLY; |
| 124 } |
89 pending_clients_.insert(client); | 125 pending_clients_.insert(client); |
90 } | 126 } |
91 } | 127 } |
92 } | 128 } |
93 | 129 |
94 // Lazily initialize the MIDI back-end. | 130 if (completion == Completion::COMPLETE_SYNCHRONOUSLY) { |
95 if (!session_is_ready) { | 131 client->CompleteStartSession(result); |
96 if (session_needs_initialization) { | 132 } else if (completion == Completion::INVOKE_INITIALIZATION) { |
97 TRACE_EVENT0("midi", "MidiManager::StartInitialization"); | 133 // Lazily initialize the MIDI back-end. |
98 session_thread_runner_ = | 134 TRACE_EVENT0("midi", "MidiManager::StartInitialization"); |
99 base::MessageLoop::current()->task_runner(); | |
100 StartInitialization(); | |
101 } | |
102 if (too_many_pending_clients_exist) { | |
103 // Return an error immediately if there are too many requests. | |
104 client->CompleteStartSession(Result::INITIALIZATION_ERROR); | |
105 return; | |
106 } | |
107 // CompleteInitialization() will be called asynchronously when platform | 135 // CompleteInitialization() will be called asynchronously when platform |
108 // dependent initialization is finished. | 136 // dependent initialization is finished. |
109 return; | 137 StartInitialization(); |
110 } | 138 } |
111 | |
112 // Platform dependent initialization was already finished for previously | |
113 // initialized clients. | |
114 Result result; | |
115 { | |
116 base::AutoLock auto_lock(lock_); | |
117 if (result_ == Result::OK) { | |
118 AddInitialPorts(client); | |
119 clients_.insert(client); | |
120 } | |
121 result = result_; | |
122 } | |
123 client->CompleteStartSession(result); | |
124 } | 139 } |
125 | 140 |
126 void MidiManager::EndSession(MidiManagerClient* client) { | 141 void MidiManager::EndSession(MidiManagerClient* client) { |
127 ReportUsage(Usage::SESSION_ENDED); | 142 ReportUsage(Usage::SESSION_ENDED); |
128 | 143 |
129 // At this point, |client| can be in the destruction process, and calling | 144 // At this point, |client| can be in the destruction process, and calling |
130 // any method of |client| is dangerous. | 145 // any method of |client| is dangerous. |
131 base::AutoLock auto_lock(lock_); | 146 base::AutoLock auto_lock(lock_); |
132 clients_.erase(client); | 147 clients_.erase(client); |
133 pending_clients_.erase(client); | 148 pending_clients_.erase(client); |
(...skipping 13 matching lines...) Expand all Loading... |
147 const std::vector<uint8>& data, | 162 const std::vector<uint8>& data, |
148 double timestamp) { | 163 double timestamp) { |
149 NOTREACHED(); | 164 NOTREACHED(); |
150 } | 165 } |
151 | 166 |
152 void MidiManager::StartInitialization() { | 167 void MidiManager::StartInitialization() { |
153 CompleteInitialization(Result::NOT_SUPPORTED); | 168 CompleteInitialization(Result::NOT_SUPPORTED); |
154 } | 169 } |
155 | 170 |
156 void MidiManager::CompleteInitialization(Result result) { | 171 void MidiManager::CompleteInitialization(Result result) { |
157 DCHECK(session_thread_runner_.get()); | 172 base::AutoLock auto_lock(lock_); |
158 // It is safe to post a task to the IO thread from here because the IO thread | 173 if (session_thread_runner_) { |
159 // should have stopped if the MidiManager is going to be destructed. | 174 session_thread_runner_->PostTask( |
160 session_thread_runner_->PostTask( | 175 FROM_HERE, base::Bind(&MidiManager::CompleteInitializationInternal, |
161 FROM_HERE, | 176 base::Unretained(this), result)); |
162 base::Bind(&MidiManager::CompleteInitializationInternal, | 177 } |
163 base::Unretained(this), | |
164 result)); | |
165 } | 178 } |
166 | 179 |
167 void MidiManager::AddInputPort(const MidiPortInfo& info) { | 180 void MidiManager::AddInputPort(const MidiPortInfo& info) { |
168 ReportUsage(Usage::INPUT_PORT_ADDED); | 181 ReportUsage(Usage::INPUT_PORT_ADDED); |
169 base::AutoLock auto_lock(lock_); | 182 base::AutoLock auto_lock(lock_); |
170 input_ports_.push_back(info); | 183 input_ports_.push_back(info); |
171 for (auto client : clients_) | 184 for (auto client : clients_) |
172 client->AddInputPort(info); | 185 client->AddInputPort(info); |
173 } | 186 } |
174 | 187 |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
235 | 248 |
236 void MidiManager::AddInitialPorts(MidiManagerClient* client) { | 249 void MidiManager::AddInitialPorts(MidiManagerClient* client) { |
237 lock_.AssertAcquired(); | 250 lock_.AssertAcquired(); |
238 | 251 |
239 for (const auto& info : input_ports_) | 252 for (const auto& info : input_ports_) |
240 client->AddInputPort(info); | 253 client->AddInputPort(info); |
241 for (const auto& info : output_ports_) | 254 for (const auto& info : output_ports_) |
242 client->AddOutputPort(info); | 255 client->AddOutputPort(info); |
243 } | 256 } |
244 | 257 |
| 258 void MidiManager::ShutdownOnSessionThread() { |
| 259 Finalize(); |
| 260 base::AutoLock auto_lock(lock_); |
| 261 finalized_ = true; |
| 262 |
| 263 // Detach all clients so that they do not call MidiManager methods any more. |
| 264 for (auto client : clients_) |
| 265 client->Detached(); |
| 266 } |
| 267 |
245 } // namespace midi | 268 } // namespace midi |
246 } // namespace media | 269 } // namespace media |
OLD | NEW |