mixer/Control: move some code to Lock*() methods

This commit is contained in:
Max Kellermann 2022-08-18 14:29:07 +02:00
parent b0873fbc90
commit 29eb3e9ebc
11 changed files with 155 additions and 144 deletions

View File

@ -170,7 +170,7 @@ ReplayGainFilter::Update()
_volume = 100;
try {
mixer_set_volume(*mixer, _volume);
mixer->LockSetVolume(_volume);
/* invoke the mixer's listener manually, just
in case the mixer implementation didn't do

View File

@ -45,7 +45,7 @@ output_mixer_get_volume(const AudioOutputControl &ao) noexcept
return -1;
try {
return mixer_get_volume(*mixer);
return mixer->LockGetVolume();
} catch (...) {
FmtError(mixer_domain,
"Failed to read mixer for '{}': {}",
@ -99,7 +99,7 @@ output_mixer_set_volume(AudioOutputControl &ao, unsigned volume)
return SetVolumeResult::DISABLED;
try {
mixer_set_volume(*mixer, volume);
mixer->LockSetVolume(volume);
return SetVolumeResult::OK;
} catch (...) {
FmtError(mixer_domain,
@ -157,7 +157,7 @@ output_mixer_get_software_volume(const AudioOutputControl &ao) noexcept
if (mixer == nullptr || !mixer->IsPlugin(software_mixer_plugin))
return -1;
return mixer_get_volume(*mixer);
return mixer->LockGetVolume();
}
int
@ -191,6 +191,6 @@ MultipleOutputs::SetSoftwareVolume(unsigned volume) noexcept
if (mixer != nullptr &&
(mixer->IsPlugin(software_mixer_plugin) ||
mixer->IsPlugin(null_mixer_plugin)))
mixer_set_volume(*mixer, volume);
mixer->LockSetVolume(volume);
}
}

View File

@ -41,92 +41,8 @@ mixer_free(Mixer *mixer) noexcept
assert(mixer != nullptr);
/* mixers with the "global" flag set might still be open at
this point (see mixer_auto_close()) */
mixer_close(*mixer);
this point (see Mixer::LockAutoClose()) */
mixer->LockClose();
delete mixer;
}
void
mixer_open(Mixer &mixer)
{
const std::scoped_lock<Mutex> protect(mixer.mutex);
if (mixer.open)
return;
try {
mixer.Open();
mixer.open = true;
mixer.failure = {};
} catch (...) {
mixer.failure = std::current_exception();
throw;
}
}
static void
mixer_close_internal(Mixer &mixer) noexcept
{
assert(mixer.open);
mixer.Close();
mixer.open = false;
mixer.failure = {};
}
void
mixer_close(Mixer &mixer) noexcept
{
const std::scoped_lock<Mutex> protect(mixer.mutex);
if (mixer.open)
mixer_close_internal(mixer);
}
void
mixer_auto_close(Mixer &mixer) noexcept
{
if (!mixer.IsGlobal())
mixer_close(mixer);
}
int
mixer_get_volume(Mixer &mixer)
{
int volume;
if (mixer.IsGlobal() && !mixer.failure)
mixer_open(mixer);
const std::scoped_lock<Mutex> protect(mixer.mutex);
if (mixer.open) {
try {
volume = mixer.GetVolume();
} catch (...) {
mixer_close_internal(mixer);
mixer.failure = std::current_exception();
throw;
}
} else
volume = -1;
return volume;
}
void
mixer_set_volume(Mixer &mixer, unsigned volume)
{
assert(volume <= 100);
if (mixer.IsGlobal() && !mixer.failure)
mixer_open(mixer);
const std::scoped_lock<Mutex> protect(mixer.mutex);
if (mixer.open)
mixer.SetVolume(volume);
else if (mixer.failure)
std::rethrow_exception(mixer.failure);
}

View File

@ -44,32 +44,4 @@ mixer_new(EventLoop &event_loop, const MixerPlugin &plugin,
void
mixer_free(Mixer *mixer) noexcept;
/**
* Throws std::runtime_error on error.
*/
void
mixer_open(Mixer &mixer);
void
mixer_close(Mixer &mixer) noexcept;
/**
* Close the mixer unless the plugin's "global" flag is set. This is
* called when the #AudioOutput is closed.
*/
void
mixer_auto_close(Mixer &mixer) noexcept;
/**
* Throws std::runtime_error on error.
*/
int
mixer_get_volume(Mixer &mixer);
/**
* Throws std::runtime_error on error.
*/
void
mixer_set_volume(Mixer &mixer, unsigned volume);
#endif

103
src/mixer/Mixer.cxx Normal file
View File

@ -0,0 +1,103 @@
/*
* Copyright 2003-2022 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "Mixer.hxx"
#include <cassert>
void
Mixer::LockOpen()
{
const std::scoped_lock lock{mutex};
if (open)
return;
_Open();
}
void
Mixer::_Open()
{
assert(!open);
try {
Open();
open = true;
failure = {};
} catch (...) {
failure = std::current_exception();
throw;
}
}
void
Mixer::LockClose() noexcept
{
const std::scoped_lock lock{mutex};
if (open)
_Close();
}
void
Mixer::_Close() noexcept
{
assert(open);
Close();
open = false;
failure = {};
}
int
Mixer::LockGetVolume()
{
if (IsGlobal() && !failure)
LockOpen();
const std::scoped_lock lock{mutex};
if (open) {
try {
return GetVolume();
} catch (...) {
_Close();
failure = std::current_exception();
throw;
}
} else
return -1;
}
void
Mixer::LockSetVolume(unsigned volume)
{
assert(volume <= 100);
if (IsGlobal() && !failure)
LockOpen();
const std::scoped_lock lock{mutex};
if (open)
SetVolume(volume);
else if (failure)
std::rethrow_exception(failure);
}

View File

@ -17,12 +17,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_MIXER_INTERNAL_HXX
#define MPD_MIXER_INTERNAL_HXX
#pragma once
#include "MixerPlugin.hxx"
#include "thread/Mutex.hxx"
#include "util/Compiler.h"
#include <exception>
@ -68,6 +66,37 @@ public:
return plugin.global;
}
/**
* Throws on error.
*/
void LockOpen();
void LockClose() noexcept;
/**
* Close the mixer unless the plugin's "global" flag is set.
* This is called when the #AudioOutput is closed.
*/
void LockAutoClose() noexcept {
if (!IsGlobal())
LockClose();
}
/**
* Throws on error.
*/
int LockGetVolume();
/**
* Throws on error.
*/
void LockSetVolume(unsigned volume);
private:
void _Open();
void _Close() noexcept;
protected:
/**
* Open mixer device
*
@ -107,5 +136,3 @@ public:
*/
virtual void SetVolume(unsigned volume) = 0;
};
#endif

View File

@ -4,6 +4,7 @@ subdir('plugins')
mixer_glue = static_library(
'mixer_glue',
'Mixer.cxx',
'Control.cxx',
'Type.cxx',
'All.cxx',

View File

@ -22,7 +22,7 @@
#include "Client.hxx"
#include "Domain.hxx"
#include "lib/fmt/ExceptionFormatter.hxx"
#include "mixer/Control.hxx"
#include "mixer/Mixer.hxx"
#include "config/Block.hxx"
#include "Log.hxx"
@ -276,7 +276,7 @@ AudioOutputControl::Open(std::unique_lock<Mutex> &lock,
if (open2 && output->mixer != nullptr) {
const ScopeUnlock unlock(mutex);
try {
mixer_open(*output->mixer);
output->mixer->LockOpen();
} catch (...) {
FmtError(output_domain,
"Failed to open mixer for '{}': {}",
@ -296,7 +296,7 @@ AudioOutputControl::CloseWait(std::unique_lock<Mutex> &lock) noexcept
return;
if (output->mixer != nullptr)
mixer_auto_close(*output->mixer);
output->mixer->LockAutoClose();
assert(!open || !fail_timer.IsDefined());
@ -359,8 +359,8 @@ AudioOutputControl::LockPauseAsync() noexcept
if (output && output->mixer != nullptr && !output->SupportsPause())
/* the device has no pause mode: close the mixer,
unless its "global" flag is set (checked by
mixer_auto_close()) */
mixer_auto_close(*output->mixer);
Mixer::LockAutoClose()) */
output->mixer->LockAutoClose();
if (output)
output->Interrupt();
@ -418,8 +418,8 @@ AudioOutputControl::LockRelease() noexcept
(!always_on || !output->SupportsPause()))
/* the device has no pause mode: close the mixer,
unless its "global" flag is set (checked by
mixer_auto_close()) */
mixer_auto_close(*output->mixer);
Mixer::LockAutoClose()) */
output->mixer->LockAutoClose();
std::unique_lock<Mutex> lock(mutex);

View File

@ -27,7 +27,7 @@
#include "OutputCommand.hxx"
#include "MultipleOutputs.hxx"
#include "Client.hxx"
#include "mixer/Control.hxx"
#include "mixer/Mixer.hxx"
#include "mixer/Memento.hxx"
#include "Idle.hxx"
@ -75,7 +75,7 @@ audio_output_disable_index(MultipleOutputs &outputs,
auto *mixer = ao.GetMixer();
if (mixer != nullptr) {
mixer_close(*mixer);
mixer->LockClose();
mixer_memento.InvalidateHardwareVolume();
idle_add(IDLE_MIXER);
}
@ -102,7 +102,7 @@ audio_output_toggle_index(MultipleOutputs &outputs,
if (!enabled) {
auto *mixer = ao.GetMixer();
if (mixer != nullptr) {
mixer_close(*mixer);
mixer->LockClose();
mixer_memento.InvalidateHardwareVolume();
idle_add(IDLE_MIXER);
}

View File

@ -19,6 +19,7 @@
#include "NullMixerListener.hxx"
#include "mixer/Control.hxx"
#include "mixer/Mixer.hxx"
#include "mixer/plugins/AlsaMixerPlugin.hxx"
#include "filter/Registry.hxx"
#include "event/Loop.hxx"
@ -57,10 +58,7 @@ try {
mixer_listener,
ConfigBlock());
mixer_open(*mixer);
volume = mixer_get_volume(*mixer);
mixer_close(*mixer);
volume = mixer->LockGetVolume();
mixer_free(mixer);
assert(volume >= -1 && volume <= 100);

View File

@ -42,12 +42,6 @@
#include <stdlib.h>
#include <stdio.h>
void
mixer_set_volume([[maybe_unused]] Mixer &mixer,
[[maybe_unused]] unsigned volume)
{
}
static std::unique_ptr<PreparedFilter>
LoadFilter(const ConfigData &config, const char *name)
{