| 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 |