Commit eefac3c1 authored by Takashi Toyoshima's avatar Takashi Toyoshima Committed by Commit Bot

Web MIDI: dynamic instantiation mode support for macOS

Use TaskService to run methods on dedicated threads, and
fix potential race condition on Finalize(). It could
cause system resource leaks that results in CoreMIDI
failing on the initialization eventually.

Bug: 694138
Change-Id: I4192e93d135f6ed8d550bdb9fecd5ff912eef7d2
Reviewed-on: https://chromium-review.googlesource.com/575794Reviewed-by: default avatarYutaka Hirano <yhirano@chromium.org>
Commit-Queue: Takashi Toyoshima <toyoshim@chromium.org>
Cr-Commit-Position: refs/heads/master@{#491004}
parent 1ef23c62
This diff is collapsed.
...@@ -7,13 +7,12 @@ ...@@ -7,13 +7,12 @@
#include <CoreMIDI/MIDIServices.h> #include <CoreMIDI/MIDIServices.h>
#include <stdint.h> #include <stdint.h>
#include <map>
#include <string>
#include <vector> #include <vector>
#include "base/callback.h" #include "base/callback.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread.h" #include "base/threading/thread.h"
#include "media/midi/midi_export.h" #include "media/midi/midi_export.h"
#include "media/midi/midi_manager.h" #include "media/midi/midi_manager.h"
...@@ -37,12 +36,6 @@ class MIDI_EXPORT MidiManagerMac final : public MidiManager { ...@@ -37,12 +36,6 @@ class MIDI_EXPORT MidiManagerMac final : public MidiManager {
double timestamp) override; double timestamp) override;
private: private:
// Runs a closure on |client_thread_|. It starts the thread if it isn't
// running and the destructor isn't called.
// Caller can bind base::Unretained(this) to |closure| since we join
// |client_thread_| in the destructor.
void RunOnClientThread(const base::Closure& closure);
// Initializes CoreMIDI on |client_thread_| asynchronously. Called from // Initializes CoreMIDI on |client_thread_| asynchronously. Called from
// StartInitialization(). // StartInitialization().
void InitializeCoreMIDI(); void InitializeCoreMIDI();
...@@ -59,7 +52,6 @@ class MIDI_EXPORT MidiManagerMac final : public MidiManager { ...@@ -59,7 +52,6 @@ class MIDI_EXPORT MidiManagerMac final : public MidiManager {
static void ReadMidiDispatch(const MIDIPacketList* packet_list, static void ReadMidiDispatch(const MIDIPacketList* packet_list,
void* read_proc_refcon, void* read_proc_refcon,
void* src_conn_refcon); void* src_conn_refcon);
virtual void ReadMidi(MIDIEndpointRef source, const MIDIPacketList *pktlist);
// An internal callback that runs on MidiSendThread. // An internal callback that runs on MidiSendThread.
void SendMidiData(MidiManagerClient* client, void SendMidiData(MidiManagerClient* client,
...@@ -67,25 +59,24 @@ class MIDI_EXPORT MidiManagerMac final : public MidiManager { ...@@ -67,25 +59,24 @@ class MIDI_EXPORT MidiManagerMac final : public MidiManager {
const std::vector<uint8_t>& data, const std::vector<uint8_t>& data,
double timestamp); double timestamp);
// CoreMIDI // CoreMIDI client reference, should be protected by |midi_client_lock_|.
MIDIClientRef midi_client_; MIDIClientRef midi_client_ = 0;
MIDIPortRef coremidi_input_; base::Lock midi_client_lock_;
MIDIPortRef coremidi_output_;
std::vector<uint8_t> midi_buffer_;
// Keeps track of the index (0-based) for each of our sources. // Following members can be accessed without any lock on kClientTaskRunner,
typedef std::map<MIDIEndpointRef, uint32_t> SourceMap; // or on I/O thread before calling BindInstance() or after calling
SourceMap source_map_; // UnbindInstance().
// Keeps track of all destinations. // CoreMIDI other references.
typedef std::vector<MIDIEndpointRef> DestinationVector; MIDIPortRef midi_input_ = 0;
DestinationVector destinations_; MIDIPortRef midi_output_ = 0;
std::vector<uint8_t> midi_buffer_;
// |client_thread_| is used to handle platform dependent operations. // Keeps track of all sources.
base::Thread client_thread_; std::vector<MIDIEndpointRef> sources_;
// Sets true on destructing object to avoid starting |client_thread_| again. // Keeps track of all destinations.
bool shutdown_; std::vector<MIDIEndpointRef> destinations_;
DISALLOW_COPY_AND_ASSIGN(MidiManagerMac); DISALLOW_COPY_AND_ASSIGN(MidiManagerMac);
}; };
......
...@@ -12,9 +12,11 @@ ...@@ -12,9 +12,11 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "media/midi/midi_service.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace midi { namespace midi {
...@@ -112,24 +114,22 @@ class FakeMidiManagerClient : public MidiManagerClient { ...@@ -112,24 +114,22 @@ class FakeMidiManagerClient : public MidiManagerClient {
class MidiManagerMacTest : public ::testing::Test { class MidiManagerMacTest : public ::testing::Test {
public: public:
MidiManagerMacTest() MidiManagerMacTest()
: manager_(new MidiManagerMac(nullptr)), : service_(base::MakeUnique<MidiService>()),
message_loop_(new base::MessageLoop) {} message_loop_(base::MakeUnique<base::MessageLoop>()) {}
~MidiManagerMacTest() override { ~MidiManagerMacTest() override {
manager_->Shutdown(); service_->Shutdown();
base::RunLoop run_loop; base::RunLoop run_loop;
run_loop.RunUntilIdle(); run_loop.RunUntilIdle();
} }
protected: protected:
void StartSession(MidiManagerClient* client) { void StartSession(MidiManagerClient* client) {
manager_->StartSession(client); service_->StartSession(client);
}
void EndSession(MidiManagerClient* client) {
manager_->EndSession(client);
} }
void EndSession(MidiManagerClient* client) { service_->EndSession(client); }
private: private:
std::unique_ptr<MidiManager> manager_; std::unique_ptr<MidiService> service_;
std::unique_ptr<base::MessageLoop> message_loop_; std::unique_ptr<base::MessageLoop> message_loop_;
DISALLOW_COPY_AND_ASSIGN(MidiManagerMacTest); DISALLOW_COPY_AND_ASSIGN(MidiManagerMacTest);
......
...@@ -58,6 +58,21 @@ bool TaskService::UnbindInstance() { ...@@ -58,6 +58,21 @@ bool TaskService::UnbindInstance() {
return true; return true;
} }
bool TaskService::IsOnTaskRunner(RunnerId runner_id) {
base::AutoLock lock(lock_);
if (bound_instance_id_ == kInvalidInstanceId)
return false;
if (runner_id == kDefaultRunnerId)
return default_task_runner_->BelongsToCurrentThread();
size_t thread = runner_id - 1;
if (threads_.size() <= thread || !threads_[thread])
return false;
return threads_[thread]->task_runner()->BelongsToCurrentThread();
}
void TaskService::PostStaticTask(RunnerId runner_id, base::OnceClosure task) { void TaskService::PostStaticTask(RunnerId runner_id, base::OnceClosure task) {
{ {
// Disallow to post a task when no instance is bound, so that new threads // Disallow to post a task when no instance is bound, so that new threads
......
...@@ -39,6 +39,9 @@ class MIDI_EXPORT TaskService final { ...@@ -39,6 +39,9 @@ class MIDI_EXPORT TaskService final {
bool BindInstance(); bool BindInstance();
bool UnbindInstance(); bool UnbindInstance();
// Checks if the current thread belongs to the specified runner.
bool IsOnTaskRunner(RunnerId runner_id);
// Posts a task to run on a specified TaskRunner. |runner_id| should be // Posts a task to run on a specified TaskRunner. |runner_id| should be
// kDefaultRunnerId or a positive number. If kDefaultRunnerId is specified // kDefaultRunnerId or a positive number. If kDefaultRunnerId is specified
// the task runs on the thread on which BindInstance() is called. Other number // the task runs on the thread on which BindInstance() is called. Other number
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment