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

Unified Diff: media/midi/dynamically_initialized_midi_manager_win.cc

Issue 2701503005: Web MIDI: device open/close for dynamic manager instantiation on Windows (Closed)
Patch Set: explicit handle reset Created 3 years, 10 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/midi/dynamically_initialized_midi_manager_win.cc
diff --git a/media/midi/dynamically_initialized_midi_manager_win.cc b/media/midi/dynamically_initialized_midi_manager_win.cc
index 26a4c835e181e62691f21b8853a0c7fa84fa9c44..2a83b006fd0ae8e2d8597b6cfd4df6b09aba4f7c 100644
--- a/media/midi/dynamically_initialized_midi_manager_win.cc
+++ b/media/midi/dynamically_initialized_midi_manager_win.cc
@@ -26,6 +26,10 @@ namespace midi {
namespace {
+// Assumes that nullptr represents an invalid MIDI handle.
+constexpr HMIDIIN kInvalidInHandle = nullptr;
+constexpr HMIDIOUT kInvalidOutHandle = nullptr;
+
// Global variables to identify MidiManager instance.
constexpr int kInvalidInstanceId = -1;
int g_active_instance_id = kInvalidInstanceId;
@@ -47,6 +51,9 @@ int IssueNextInstanceId() {
constexpr int kTaskRunner = 0;
// Obtains base::Lock instance pointer to ensure tasks run safely on TaskRunner.
+// Since all tasks on TaskRunner run behind a lock of *GetTaskLock(), we can
+// access all members even on the I/O thread if a lock of *GetTaskLock() is
+// obtained.
base::Lock* GetTaskLock() {
static base::Lock* lock = new base::Lock;
return lock;
@@ -69,6 +76,36 @@ void RunTask(int instance_id, const base::Closure& task) {
// TODO(toyoshim): Factor out TaskRunner related functionaliries above, and
// deprecate MidiScheduler. It should be available via MidiManager::scheduler().
+// Helper functions to close MIDI device handles on TaskRunner asynchronously.
+void FinalizeInPort(HMIDIIN handle) {
+ midiInClose(handle);
+}
+
+void FinalizeOutPort(HMIDIOUT handle) {
+ midiOutClose(handle);
+}
+
+// Handles MIDI input port callbacks that runs on a system provided thread.
+void CALLBACK HandleMidiInCallback(HMIDIIN hmi,
+ UINT msg,
+ DWORD_PTR instance,
+ DWORD_PTR param1,
+ DWORD_PTR param2) {
+ // TODO(toyoshim): Following patches will implement actual functions.
+}
+
+// Handles MIDI output port callbacks that runs on a system provided thread.
+void CALLBACK HandleMidiOutCallback(HMIDIOUT hmo,
+ UINT msg,
+ DWORD_PTR instance,
+ DWORD_PTR param1,
+ DWORD_PTR param2) {
+ // TODO(toyoshim): Following patches will implement actual functions.
+}
+
+// All instances of Port subclasses are always accessed behind a lock of
+// *GetTaskLock(). Port and subclasses implementation do not need to
+// consider thread safety.
class Port {
public:
Port(const std::string& type,
@@ -162,7 +199,8 @@ class DynamicallyInitializedMidiManagerWin::InPort final : public Port {
caps.wPid,
caps.vDriverVersion,
base::WideToUTF8(
- base::string16(caps.szPname, wcslen(caps.szPname)))) {}
+ base::string16(caps.szPname, wcslen(caps.szPname)))),
+ in_handle_(kInvalidInHandle) {}
static std::vector<std::unique_ptr<InPort>> EnumerateActivePorts() {
std::vector<std::unique_ptr<InPort>> ports;
@@ -180,6 +218,13 @@ class DynamicallyInitializedMidiManagerWin::InPort final : public Port {
return ports;
}
+ void Finalize(scoped_refptr<base::SingleThreadTaskRunner> runner) {
+ if (in_handle_ != kInvalidInHandle) {
+ runner->PostTask(FROM_HERE, base::Bind(&FinalizeInPort, in_handle_));
+ in_handle_ = kInvalidInHandle;
+ }
+ }
+
void NotifyPortStateSet(DynamicallyInitializedMidiManagerWin* manager) {
manager->PostReplyTask(
base::Bind(&DynamicallyInitializedMidiManagerWin::SetInputPortState,
@@ -191,6 +236,34 @@ class DynamicallyInitializedMidiManagerWin::InPort final : public Port {
base::Bind(&DynamicallyInitializedMidiManagerWin::AddInputPort,
base::Unretained(manager), info_));
}
+
+ // Port overrides:
+ bool Disconnect() override {
+ if (in_handle_ != kInvalidInHandle) {
+ // Following API call may fail because device was already disconnected.
+ // But just in case.
+ midiInClose(in_handle_);
+ in_handle_ = kInvalidInHandle;
+ }
+ return Port::Disconnect();
+ }
+
+ void Open() override {
+ // TODO(toyoshim): Pass instance_id to implement HandleMidiInCallback.
+ MMRESULT result =
+ midiInOpen(&in_handle_, device_id_,
+ reinterpret_cast<DWORD_PTR>(&HandleMidiInCallback), 0,
+ CALLBACK_FUNCTION);
+ if (result == MMSYSERR_NOERROR) {
+ Port::Open();
+ } else {
+ in_handle_ = kInvalidInHandle;
+ Disconnect();
+ }
+ }
+
+ private:
+ HMIDIIN in_handle_;
};
// TODO(toyoshim): Following patches will implement actual functions.
@@ -204,7 +277,8 @@ class DynamicallyInitializedMidiManagerWin::OutPort final : public Port {
caps.vDriverVersion,
base::WideToUTF8(
base::string16(caps.szPname, wcslen(caps.szPname)))),
- software_(caps.wTechnology == MOD_SWSYNTH) {}
+ software_(caps.wTechnology == MOD_SWSYNTH),
+ out_handle_(kInvalidOutHandle) {}
static std::vector<std::unique_ptr<OutPort>> EnumerateActivePorts() {
std::vector<std::unique_ptr<OutPort>> ports;
@@ -222,6 +296,25 @@ class DynamicallyInitializedMidiManagerWin::OutPort final : public Port {
return ports;
}
+ void Finalize(scoped_refptr<base::SingleThreadTaskRunner> runner) {
+ if (out_handle_ != kInvalidOutHandle) {
+ runner->PostTask(FROM_HERE, base::Bind(&FinalizeOutPort, out_handle_));
+ out_handle_ = kInvalidOutHandle;
+ }
+ }
+
+ void NotifyPortStateSet(DynamicallyInitializedMidiManagerWin* manager) {
+ manager->PostReplyTask(
+ base::Bind(&DynamicallyInitializedMidiManagerWin::SetOutputPortState,
+ base::Unretained(manager), index_, info_.state));
+ }
+
+ void NotifyPortAdded(DynamicallyInitializedMidiManagerWin* manager) {
+ manager->PostReplyTask(
+ base::Bind(&DynamicallyInitializedMidiManagerWin::AddOutputPort,
+ base::Unretained(manager), info_));
+ }
+
// Port overrides:
bool Connect() override {
// Until |software| option is supported, disable Microsoft GS Wavetable
@@ -234,20 +327,31 @@ class DynamicallyInitializedMidiManagerWin::OutPort final : public Port {
return Port::Connect();
}
- // Port Overrides:
- void NotifyPortStateSet(DynamicallyInitializedMidiManagerWin* manager) {
- manager->PostReplyTask(
- base::Bind(&DynamicallyInitializedMidiManagerWin::SetOutputPortState,
- base::Unretained(manager), index_, info_.state));
+ bool Disconnect() override {
+ if (out_handle_ != kInvalidOutHandle) {
+ // Following API call may fail because device was already disconnected.
+ // But just in case.
+ midiOutClose(out_handle_);
+ out_handle_ = kInvalidOutHandle;
+ }
+ return Port::Disconnect();
}
- void NotifyPortAdded(DynamicallyInitializedMidiManagerWin* manager) {
- manager->PostReplyTask(
- base::Bind(&DynamicallyInitializedMidiManagerWin::AddOutputPort,
- base::Unretained(manager), info_));
+ void Open() override {
+ MMRESULT result =
+ midiOutOpen(&out_handle_, device_id_,
+ reinterpret_cast<DWORD_PTR>(&HandleMidiOutCallback), 0,
+ CALLBACK_FUNCTION);
+ if (result == MMSYSERR_NOERROR) {
+ Port::Open();
+ } else {
+ out_handle_ = kInvalidOutHandle;
+ Disconnect();
+ }
}
const bool software_;
+ HMIDIOUT out_handle_;
};
DynamicallyInitializedMidiManagerWin::DynamicallyInitializedMidiManagerWin(
@@ -304,7 +408,18 @@ void DynamicallyInitializedMidiManagerWin::Finalize() {
// Ensures that no task runs on TaskRunner so to destruct the instance safely.
// Tasks that did not started yet will do nothing after invalidate the
// instance ID above.
+ // Behind the lock below, we can safely access all members for finalization
+ // even on the I/O thread.
base::AutoLock lock(*GetTaskLock());
+
+ // Posts tasks that finalize each device port without MidiManager instance
+ // on TaskRunner. If another MidiManager instance is created, its
+ // initialization runs on the same task runner after all tasks posted here
+ // finish.
+ for (const auto& port : input_ports_)
+ port->Finalize(service()->GetTaskRunner(kTaskRunner));
+ for (const auto& port : output_ports_)
+ port->Finalize(service()->GetTaskRunner(kTaskRunner));
}
void DynamicallyInitializedMidiManagerWin::DispatchSendMidiData(
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698