Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(171)

Side by Side Diff: media/midi/midi_manager.cc

Issue 1315793008: Web MIDI: introduce MidiManager::Shutdown to shutdown gracefully (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rename to Detach Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/midi/midi_manager.h ('k') | media/midi/midi_manager_mac_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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->Detach();
266 }
267
245 } // namespace midi 268 } // namespace midi
246 } // namespace media 269 } // namespace media
OLDNEW
« no previous file with comments | « media/midi/midi_manager.h ('k') | media/midi/midi_manager_mac_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698