diff options
author | Glenn Kasten <gkasten@google.com> | 2014-09-05 10:34:40 -0700 |
---|---|---|
committer | Glenn Kasten <gkasten@google.com> | 2014-09-08 13:31:19 -0700 |
commit | 22f4563456425e098765c6f196053acd904a8b17 (patch) | |
tree | 4cc54727c82c225118cd3d8d83d941f253729273 | |
parent | 6408391200f3bce59f5069cc016a2fe3dd3b0272 (diff) | |
download | manta-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.c | 147 |
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); } |