summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Kasten <gkasten@google.com>2014-09-05 10:34:40 -0700
committerGlenn Kasten <gkasten@google.com>2014-09-08 13:31:19 -0700
commit22f4563456425e098765c6f196053acd904a8b17 (patch)
tree4cc54727c82c225118cd3d8d83d941f253729273
parent6408391200f3bce59f5069cc016a2fe3dd3b0272 (diff)
downloadmanta-22f4563456425e098765c6f196053acd904a8b17.tar.gz
Reverse order of stream and adev locks
This now matches hardware/qcom/audio implementation, and fixes a bug where a slow input can cause output to underrun. However, it re-introduces a less important bug where parameter changes can be starved. Also: - add generic comments - do_out_standby and do_in_standby now return void Bug: 17129712 Change-Id: Ic95fe3c555b40c2b798af943404bd6a7bff54fbe
-rw-r--r--audio/audio_hw.c147
1 files changed, 90 insertions, 57 deletions
diff --git a/audio/audio_hw.c b/audio/audio_hw.c
index 6bc568d..34e1f2c 100644
--- a/audio/audio_hw.c
+++ b/audio/audio_hw.c
@@ -144,12 +144,13 @@ struct audio_device {
int es305_preset;
int es305_new_mode;
int es305_mode;
- int hdmi_drv_fd;
+ int hdmi_drv_fd; /* either an fd >= 0 or -1 */
struct bubble_level *bubble_level;
audio_channel_mask_t in_channel_mask;
unsigned int sco_on_count;
struct stream_out *outputs[OUTPUT_TOTAL];
+ pthread_mutex_t lock_outputs; /* see note below on mutex acquisition order */
};
struct stream_out {
@@ -422,11 +423,15 @@ const struct route_config * const route_configs[IN_SOURCE_TAB_SIZE]
}
};
-static int do_out_standby(struct stream_out *out);
+static void do_out_standby(struct stream_out *out);
/**
- * NOTE: when multiple mutexes have to be acquired, always respect the
- * following order: hw device > in stream > out stream
+ * NOTE: when multiple mutexes have to be acquired, always respect the following order:
+ * lock_outputs for hw device outputs list only
+ * in stream
+ * out stream(s) in enum output_type order
+ * hw device
+ * TODO investigate whether we ever actually take both in stream and out stream
*/
/* Helper functions */
@@ -620,9 +625,7 @@ bool get_bubblelevel(struct audio_device *adev)
return (adev->bubble_level != NULL);
}
-/* Must be called with HDMI out stream and hw device mutexes locked,
- * and all other out stream mutexes unlocked.
- */
+/* must be called with hw device outputs list, all out streams, and hw device mutexes locked */
static void force_non_hdmi_out_standby(struct audio_device *adev)
{
enum output_type type;
@@ -632,9 +635,8 @@ static void force_non_hdmi_out_standby(struct audio_device *adev)
out = adev->outputs[type];
if (type == OUTPUT_HDMI || !out)
continue;
- pthread_mutex_lock(&out->lock);
+ /* This will never recurse more than 2 levels deep. */
do_out_standby(out);
- pthread_mutex_unlock(&out->lock);
}
}
@@ -701,7 +703,7 @@ static void stop_bt_sco(struct audio_device *adev) {
pcm_close(adev->pcm_sco_in);
}
-/* must be called with hw device and output stream mutexes locked */
+/* must be called with hw device outputs list, output stream, and hw device mutexes locked */
static int start_output_stream(struct stream_out *out)
{
struct audio_device *adev = out->dev;
@@ -762,7 +764,7 @@ static int start_output_stream(struct stream_out *out)
return 0;
}
-/* must be called with hw device and input stream mutexes locked */
+/* must be called with input stream and hw device mutexes locked */
static int start_input_stream(struct stream_in *in)
{
struct audio_device *adev = in->dev;
@@ -975,6 +977,7 @@ static audio_devices_t output_devices(struct stream_out *out)
for (type = 0; type < OUTPUT_TOTAL; ++type) {
struct stream_out *other = dev->outputs[type];
if (other && (other != out) && !other->standby) {
+ // TODO no longer accurate
/* safe to access other stream without a mutex,
* because we hold the dev lock,
* which prevents the other stream from being closed
@@ -986,8 +989,8 @@ static audio_devices_t output_devices(struct stream_out *out)
return devices;
}
-/* must be called with out stream and hw device mutex locked */
-static int do_out_standby(struct stream_out *out)
+/* must be called with hw device outputs list, all out streams, and hw device mutex locked */
+static void do_out_standby(struct stream_out *out)
{
struct audio_device *adev = out->dev;
int i;
@@ -1017,24 +1020,47 @@ static int do_out_standby(struct stream_out *out)
if (adev->out_device)
select_devices(adev);
}
+}
- return 0;
+/* lock outputs list, all output streams, and device */
+static void lock_all_outputs(struct audio_device *adev)
+{
+ enum output_type type;
+ pthread_mutex_lock(&adev->lock_outputs);
+ for (type = 0; type < OUTPUT_TOTAL; ++type) {
+ struct stream_out *out = adev->outputs[type];
+ if (out)
+ pthread_mutex_lock(&out->lock);
+ }
+ pthread_mutex_lock(&adev->lock);
+}
+
+/* unlock device, all output streams (except specified stream), and outputs list */
+static void unlock_all_outputs(struct audio_device *adev, struct stream_out *except)
+{
+ /* unlock order is irrelevant, but for cleanliness we unlock in reverse order */
+ pthread_mutex_unlock(&adev->lock);
+ enum output_type type = OUTPUT_TOTAL;
+ do {
+ struct stream_out *out = adev->outputs[--type];
+ if (out && out != except)
+ pthread_mutex_unlock(&out->lock);
+ } while (type != (enum output_type) 0);
+ pthread_mutex_unlock(&adev->lock_outputs);
}
static int out_standby(struct audio_stream *stream)
{
struct stream_out *out = (struct stream_out *)stream;
- int ret;
+ struct audio_device *adev = out->dev;
- pthread_mutex_lock(&out->dev->lock);
- pthread_mutex_lock(&out->lock);
+ lock_all_outputs(adev);
- ret = do_out_standby(out);
+ do_out_standby(out);
- pthread_mutex_unlock(&out->lock);
- pthread_mutex_unlock(&out->dev->lock);
+ unlock_all_outputs(adev, NULL);
- return ret;
+ return 0;
}
static int out_dump(const struct audio_stream *stream, int fd)
@@ -1055,8 +1081,7 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
value, sizeof(value));
- pthread_mutex_lock(&adev->lock);
- pthread_mutex_lock(&out->lock);
+ lock_all_outputs(adev);
if (ret >= 0) {
val = atoi(value);
if ((out->device != val) && (val != 0)) {
@@ -1083,8 +1108,7 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
out->device = val;
}
}
- pthread_mutex_unlock(&out->lock);
- pthread_mutex_unlock(&adev->lock);
+ unlock_all_outputs(adev, NULL);
str_parms_destroy(parms);
return ret;
@@ -1144,7 +1168,13 @@ static int out_set_volume(struct audio_stream_out *stream, float left,
struct stream_out *out = (struct stream_out *)stream;
struct audio_device *adev = out->dev;
- if (out == adev->outputs[OUTPUT_HDMI]) {
+ /* The mutex lock is not needed, because the client
+ * is not allowed to close the stream concurrently with this API
+ * pthread_mutex_lock(&adev->lock_outputs);
+ */
+ bool is_HDMI = out == adev->outputs[OUTPUT_HDMI];
+ /* pthread_mutex_unlock(&adev->lock_outputs); */
+ if (is_HDMI) {
/* only take left channel into account: the API is for stereo anyway */
out->muted = (left == 0.0f);
return 0;
@@ -1160,23 +1190,29 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
struct audio_device *adev = out->dev;
int i;
- /*
+ /* FIXME This comment is no longer correct
* acquiring hw device mutex systematically is useful if a low
* priority thread is waiting on the output stream mutex - e.g.
* executing out_set_parameters() while holding the hw device
* mutex
*/
- pthread_mutex_lock(&adev->lock);
pthread_mutex_lock(&out->lock);
if (out->standby) {
+ pthread_mutex_unlock(&out->lock);
+ lock_all_outputs(adev);
+ if (!out->standby) {
+ unlock_all_outputs(adev, out);
+ goto false_alarm;
+ }
ret = start_output_stream(out);
- if (ret != 0) {
- pthread_mutex_unlock(&adev->lock);
- goto exit;
+ if (ret < 0) {
+ unlock_all_outputs(adev, NULL);
+ goto final_exit;
}
out->standby = false;
+ unlock_all_outputs(adev, out);
}
- pthread_mutex_unlock(&adev->lock);
+false_alarm:
if (out->disabled) {
ret = -EPIPE;
@@ -1198,6 +1234,7 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
exit:
pthread_mutex_unlock(&out->lock);
+final_exit:
if (ret != 0) {
usleep(bytes * 1000000 / audio_stream_out_frame_size(stream) /
@@ -1305,7 +1342,7 @@ static int in_set_format(struct audio_stream *stream, audio_format_t format)
}
/* must be called with in stream and hw device mutex locked */
-static int do_in_standby(struct stream_in *in)
+static void do_in_standby(struct stream_in *in)
{
struct audio_device *adev = in->dev;
@@ -1327,23 +1364,21 @@ static int do_in_standby(struct stream_in *in)
}
eS305_SetActiveIoHandle(ES305_IO_HANDLE_NONE);
- return 0;
}
static int in_standby(struct audio_stream *stream)
{
struct stream_in *in = (struct stream_in *)stream;
- int ret;
- pthread_mutex_lock(&in->dev->lock);
pthread_mutex_lock(&in->lock);
+ pthread_mutex_lock(&in->dev->lock);
- ret = do_in_standby(in);
+ do_in_standby(in);
- pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&in->dev->lock);
+ pthread_mutex_unlock(&in->lock);
- return ret;
+ return 0;
}
static int in_dump(const struct audio_stream *stream, int fd)
@@ -1363,8 +1398,8 @@ static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
parms = str_parms_create_str(kvpairs);
- pthread_mutex_lock(&adev->lock);
pthread_mutex_lock(&in->lock);
+ pthread_mutex_lock(&adev->lock);
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE,
value, sizeof(value));
if (ret >= 0) {
@@ -1399,8 +1434,8 @@ static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
select_devices(adev);
}
- pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&adev->lock);
+ pthread_mutex_unlock(&in->lock);
str_parms_destroy(parms);
return ret;
@@ -1458,17 +1493,15 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
* executing in_set_parameters() while holding the hw device
* mutex
*/
- pthread_mutex_lock(&adev->lock);
pthread_mutex_lock(&in->lock);
if (in->standby) {
+ pthread_mutex_lock(&adev->lock);
ret = start_input_stream(in);
- if (ret == 0)
- in->standby = 0;
+ pthread_mutex_unlock(&adev->lock);
+ if (ret < 0)
+ goto exit;
+ in->standby = false;
}
- pthread_mutex_unlock(&adev->lock);
-
- if (ret < 0)
- goto exit;
/*if (in->num_preprocessors != 0)
ret = process_frames(in, buffer, frames_rq);
@@ -1509,13 +1542,13 @@ static int in_add_audio_effect(const struct audio_stream *stream,
effect_descriptor_t descr;
if ((*effect)->get_descriptor(effect, &descr) == 0) {
- pthread_mutex_lock(&in->dev->lock);
pthread_mutex_lock(&in->lock);
+ pthread_mutex_lock(&in->dev->lock);
eS305_AddEffect(&descr, in->io_handle);
- pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&in->dev->lock);
+ pthread_mutex_unlock(&in->lock);
}
return 0;
@@ -1528,13 +1561,13 @@ static int in_remove_audio_effect(const struct audio_stream *stream,
effect_descriptor_t descr;
if ((*effect)->get_descriptor(effect, &descr) == 0) {
- pthread_mutex_lock(&in->dev->lock);
pthread_mutex_lock(&in->lock);
+ pthread_mutex_lock(&in->dev->lock);
eS305_RemoveEffect(&descr, in->io_handle);
- pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&in->dev->lock);
+ pthread_mutex_unlock(&in->lock);
}
return 0;
@@ -1619,14 +1652,14 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
/* out->muted = false; by calloc() */
/* out->written = 0; by calloc() */
- pthread_mutex_lock(&adev->lock);
+ pthread_mutex_lock(&adev->lock_outputs);
if (adev->outputs[type]) {
- pthread_mutex_unlock(&adev->lock);
+ pthread_mutex_unlock(&adev->lock_outputs);
ret = -EBUSY;
goto err_open;
}
adev->outputs[type] = out;
- pthread_mutex_unlock(&adev->lock);
+ pthread_mutex_unlock(&adev->lock_outputs);
*stream_out = &out->stream;
@@ -1646,14 +1679,14 @@ static void adev_close_output_stream(struct audio_hw_device *dev,
out_standby(&stream->common);
adev = (struct audio_device *)dev;
- pthread_mutex_lock(&adev->lock);
+ pthread_mutex_lock(&adev->lock_outputs);
for (type = 0; type < OUTPUT_TOTAL; ++type) {
if (adev->outputs[type] == (struct stream_out *) stream) {
adev->outputs[type] = NULL;
break;
}
}
- pthread_mutex_unlock(&adev->lock);
+ pthread_mutex_unlock(&adev->lock_outputs);
free(stream);
}