| Index: media/midi/midi_manager_alsa.cc
|
| diff --git a/media/midi/midi_manager_alsa.cc b/media/midi/midi_manager_alsa.cc
|
| index d99751bff01be9161f404b4681d980b7b54d839d..05efb49be60fd66817a44cd2e928d6646ef53dd6 100644
|
| --- a/media/midi/midi_manager_alsa.cc
|
| +++ b/media/midi/midi_manager_alsa.cc
|
| @@ -15,6 +15,7 @@
|
|
|
| #include "base/bind.h"
|
| #include "base/json/json_string_value_serializer.h"
|
| +#include "base/lazy_instance.h"
|
| #include "base/logging.h"
|
| #include "base/macros.h"
|
| #include "base/memory/ptr_util.h"
|
| @@ -27,6 +28,7 @@
|
| #include "base/time/time.h"
|
| #include "crypto/sha2.h"
|
| #include "media/midi/midi_port_info.h"
|
| +#include "media/midi/midi_service.h"
|
|
|
| namespace midi {
|
|
|
| @@ -35,6 +37,10 @@ namespace {
|
| using mojom::PortState;
|
| using mojom::Result;
|
|
|
| +// TODO(toyoshim): use constexpr for following const values.
|
| +const int kEventTaskRunner = 0;
|
| +const int kSendTaskRunner = 1;
|
| +
|
| // Per-output buffer. This can be smaller, but then large sysex messages
|
| // will be (harmlessly) split across multiple seq events. This should
|
| // not have any real practical effect, except perhaps to slightly reorder
|
| @@ -90,6 +96,17 @@ const unsigned int kCreateInputPortCaps =
|
| const unsigned int kCreatePortType =
|
| SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION;
|
|
|
| +// Global variables to identify MidiManagerAlsa instance.
|
| +const int kInvalidInstanceId = -1;
|
| +int g_active_instance_id = kInvalidInstanceId;
|
| +int g_next_instance_id = 0;
|
| +base::LazyInstance<base::Lock> g_instance_id_lock = LAZY_INSTANCE_INITIALIZER;
|
| +
|
| +// Prevent current instance from quiting Finalize() while tasks run on external
|
| +// TaskRunners.
|
| +base::LazyInstance<base::Lock> g_event_task_lock = LAZY_INSTANCE_INITIALIZER;
|
| +base::LazyInstance<base::Lock> g_send_task_lock = LAZY_INSTANCE_INITIALIZER;
|
| +
|
| int AddrToInt(int client, int port) {
|
| return (client << 8) | port;
|
| }
|
| @@ -153,8 +170,7 @@ void SetStringIfNonEmpty(base::DictionaryValue* value,
|
|
|
| } // namespace
|
|
|
| -MidiManagerAlsa::MidiManagerAlsa()
|
| - : event_thread_("MidiEventThread"), send_thread_("MidiSendThread") {}
|
| +MidiManagerAlsa::MidiManagerAlsa(MidiService* service) : MidiManager(service) {}
|
|
|
| MidiManagerAlsa::~MidiManagerAlsa() {
|
| // Take lock to ensure that the members initialized on the IO thread
|
| @@ -169,11 +185,18 @@ MidiManagerAlsa::~MidiManagerAlsa() {
|
| CHECK(!udev_);
|
| CHECK(!udev_monitor_);
|
|
|
| - CHECK(!send_thread_.IsRunning());
|
| - CHECK(!event_thread_.IsRunning());
|
| + base::AutoLock instance_id_lock(g_instance_id_lock.Get());
|
| + CHECK_EQ(kInvalidInstanceId, g_active_instance_id);
|
| }
|
|
|
| void MidiManagerAlsa::StartInitialization() {
|
| + {
|
| + base::AutoLock lock(g_instance_id_lock.Get());
|
| + CHECK_EQ(kInvalidInstanceId, g_active_instance_id);
|
| + instance_id_ = g_next_instance_id++;
|
| + g_active_instance_id = instance_id_;
|
| + }
|
| +
|
| base::AutoLock lock(lazy_init_member_lock_);
|
|
|
| initialization_thread_checker_.reset(new base::ThreadChecker());
|
| @@ -288,11 +311,10 @@ void MidiManagerAlsa::StartInitialization() {
|
|
|
| // Start processing events. Don't do this before enumeration of both
|
| // ALSA and udev.
|
| - event_thread_.Start();
|
| - event_thread_.task_runner()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&MidiManagerAlsa::ScheduleEventLoop, base::Unretained(this)));
|
| - send_thread_.Start();
|
| + service()
|
| + ->GetTaskRunner(kEventTaskRunner)
|
| + ->PostTask(FROM_HERE, base::Bind(&MidiManagerAlsa::EventLoop,
|
| + base::Unretained(this), instance_id_));
|
|
|
| CompleteInitialization(Result::OK);
|
| }
|
| @@ -301,23 +323,24 @@ void MidiManagerAlsa::Finalize() {
|
| base::AutoLock lock(lazy_init_member_lock_);
|
| DCHECK(initialization_thread_checker_->CalledOnValidThread());
|
|
|
| - // Tell the event thread it will soon be time to shut down. This gives
|
| - // us assurance the thread will stop in case the SND_SEQ_EVENT_CLIENT_EXIT
|
| - // message is lost.
|
| + // Tell tasks running on TaskRunners it will soon be time to shut down. This
|
| + // gives us assurance a task running on kEventTaskRunner will stop in case the
|
| + // SND_SEQ_EVENT_CLIENT_EXIT message is lost.
|
| {
|
| - base::AutoLock lock(shutdown_lock_);
|
| - event_thread_shutdown_ = true;
|
| + base::AutoLock lock(g_instance_id_lock.Get());
|
| + CHECK_EQ(instance_id_, g_active_instance_id);
|
| + g_active_instance_id = kInvalidInstanceId;
|
| }
|
|
|
| - // Stop the send thread.
|
| - send_thread_.Stop();
|
| + // Ensure that no tasks run on kSendTaskRunner.
|
| + base::AutoLock send_runner_lock(g_send_task_lock.Get());
|
|
|
| // Close the out client. This will trigger the event thread to stop,
|
| // because of SND_SEQ_EVENT_CLIENT_EXIT.
|
| out_client_.reset();
|
|
|
| - // Wait for the event thread to stop.
|
| - event_thread_.Stop();
|
| + // Ensure that no tasks run on kEventTaskRunner.
|
| + base::AutoLock event_runner_lock(g_event_task_lock.Get());
|
|
|
| // Destruct the other stuff we initialized in StartInitialization().
|
| udev_monitor_.reset();
|
| @@ -339,15 +362,13 @@ void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
|
| delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
|
| }
|
|
|
| - send_thread_.task_runner()->PostDelayedTask(
|
| - FROM_HERE, base::Bind(&MidiManagerAlsa::SendMidiData,
|
| - base::Unretained(this), port_index, data),
|
| - delay);
|
| -
|
| - // Acknowledge send.
|
| - send_thread_.task_runner()->PostTask(
|
| - FROM_HERE, base::Bind(&MidiManagerAlsa::AccumulateMidiBytesSent,
|
| - base::Unretained(this), client, data.size()));
|
| + service()
|
| + ->GetTaskRunner(kSendTaskRunner)
|
| + ->PostDelayedTask(
|
| + FROM_HERE,
|
| + base::Bind(&MidiManagerAlsa::SendMidiData, base::Unretained(this),
|
| + instance_id_, client, port_index, data),
|
| + delay);
|
| }
|
|
|
| MidiManagerAlsa::MidiPort::Id::Id() = default;
|
| @@ -864,9 +885,22 @@ std::string MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
|
| return "";
|
| }
|
|
|
| -void MidiManagerAlsa::SendMidiData(uint32_t port_index,
|
| +void MidiManagerAlsa::SendMidiData(int instance_id,
|
| + MidiManagerClient* client,
|
| + uint32_t port_index,
|
| const std::vector<uint8_t>& data) {
|
| - DCHECK(send_thread_.task_runner()->BelongsToCurrentThread());
|
| + DCHECK(service()->GetTaskRunner(kSendTaskRunner)->BelongsToCurrentThread());
|
| +
|
| + // Obtain the lock so that the instance could not be destructed while this
|
| + // method is running on the kSendTaskRunner.
|
| + base::AutoLock lock(g_send_task_lock.Get());
|
| + {
|
| + // Check if Finalize() already runs. After this check, we can access |this|
|
| + // safely on the kEventTaskRunner.
|
| + base::AutoLock instance_id_lock(g_instance_id_lock.Get());
|
| + if (instance_id != g_active_instance_id)
|
| + return;
|
| + }
|
|
|
| snd_midi_event_t* encoder;
|
| snd_midi_event_new(kSendBufferSize, &encoder);
|
| @@ -886,15 +920,23 @@ void MidiManagerAlsa::SendMidiData(uint32_t port_index,
|
| }
|
| }
|
| snd_midi_event_free(encoder);
|
| -}
|
|
|
| -void MidiManagerAlsa::ScheduleEventLoop() {
|
| - event_thread_.task_runner()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
|
| + // Acknowledge send.
|
| + AccumulateMidiBytesSent(client, data.size());
|
| }
|
|
|
| -void MidiManagerAlsa::EventLoop() {
|
| +void MidiManagerAlsa::EventLoop(int instance_id) {
|
| + // Obtain the lock so that the instance could not be destructed while this
|
| + // method is running on the kEventTaskRunner.
|
| + base::AutoLock lock(g_event_task_lock.Get());
|
| + {
|
| + // Check if Finalize() already runs. After this check, we can access |this|
|
| + // safely on the kEventTaskRunner.
|
| + base::AutoLock instance_id_lock(g_instance_id_lock.Get());
|
| + if (instance_id != g_active_instance_id)
|
| + return;
|
| + }
|
| +
|
| bool loop_again = true;
|
|
|
| struct pollfd pfd[2];
|
| @@ -922,9 +964,6 @@ void MidiManagerAlsa::EventLoop() {
|
| VLOG(1) << "snd_seq_event_input detected buffer overrun";
|
| // We've lost events: check another way to see if we need to shut
|
| // down.
|
| - base::AutoLock lock(shutdown_lock_);
|
| - if (event_thread_shutdown_)
|
| - loop_again = false;
|
| } else if (err == -EAGAIN) {
|
| // We've read all the data.
|
| } else if (err < 0) {
|
| @@ -973,8 +1012,12 @@ void MidiManagerAlsa::EventLoop() {
|
| }
|
|
|
| // Do again.
|
| - if (loop_again)
|
| - ScheduleEventLoop();
|
| + if (loop_again) {
|
| + service()
|
| + ->GetTaskRunner(kEventTaskRunner)
|
| + ->PostTask(FROM_HERE, base::Bind(&MidiManagerAlsa::EventLoop,
|
| + base::Unretained(this), instance_id));
|
| + }
|
| }
|
|
|
| void MidiManagerAlsa::ProcessSingleEvent(snd_seq_event_t* event,
|
| @@ -1401,8 +1444,8 @@ bool MidiManagerAlsa::Subscribe(uint32_t port_index,
|
| return true;
|
| }
|
|
|
| -MidiManager* MidiManager::Create() {
|
| - return new MidiManagerAlsa();
|
| +MidiManager* MidiManager::Create(MidiService* service) {
|
| + return new MidiManagerAlsa(service);
|
| }
|
|
|
| } // namespace midi
|
|
|