Bug461936 - Update to libsydneyaudio r3760 to fix deadlock when shutting down a stream - rs=roc

This commit is contained in:
Chris Double 2008-11-04 21:23:55 +13:00
parent 53052721df
commit 94f1ba7c43
4 changed files with 192 additions and 35 deletions

View File

@ -5,4 +5,4 @@ the Mozilla build system.
http://svn.annodex.net/libsydneyaudio/trunk
The svn revision number used was r3733.
The svn revision number used was r3760.

View File

@ -260,10 +260,15 @@ sa_stream_destroy(sa_stream_t *s) {
return SA_SUCCESS;
}
pthread_mutex_lock(&s->mutex);
/*
* Shut down the audio output device.
* Shut down the audio output device. Don't hold the mutex when stopping
* the audio device, because it is possible to deadlock with this thread
* holding mutex then waiting on an internal Core Audio lock, and with the
* callback thread holding the Core Audio lock and waiting on the mutex.
* This does not need to be protected by the mutex anyway because
* AudioOutputUnitStop, when called from the non-callback thread, blocks
* until in-flight callbacks complete and the HAL shuts down. See:
* http://lists.apple.com/archives/coreaudio-api/2005/Dec/msg00055.html
*/
int result = SA_SUCCESS;
if (s->output_unit != NULL) {
@ -278,8 +283,6 @@ sa_stream_destroy(sa_stream_t *s) {
}
}
pthread_mutex_unlock(&s->mutex);
/*
* Release resources.
*/
@ -546,9 +549,14 @@ sa_stream_pause(sa_stream_t *s) {
return SA_ERROR_NO_INIT;
}
pthread_mutex_lock(&s->mutex);
/*
* Don't hold the mutex when stopping the audio device, because it is
* possible to deadlock with this thread holding mutex then waiting on an
* internal Core Audio lock, and with the callback thread holding the Core
* Audio lock and waiting on the mutex.
*/
AudioOutputUnitStop(s->output_unit);
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
@ -561,15 +569,21 @@ sa_stream_resume(sa_stream_t *s) {
}
pthread_mutex_lock(&s->mutex);
/*
* The audio device resets its mSampleTime counter after pausing,
* so we need to clear our tracking value to keep that in sync.
*/
s->bytes_played = 0;
pthread_mutex_unlock(&s->mutex);
/*
* Don't hold the mutex when starting the audio device, because it is
* possible to deadlock with this thread holding mutex then waiting on an
* internal Core Audio lock, and with the callback thread holding the Core
* Audio lock and waiting on the mutex.
*/
AudioOutputUnitStart(s->output_unit);
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}

View File

@ -51,9 +51,20 @@
#define LOOP_WHILE_EINTR(v,func) do { (v) = (func); } \
while ((v) == -1 && errno == EINTR);
typedef struct sa_buf sa_buf;
struct sa_buf {
unsigned int size; /* the size of data */
sa_buf *next;
unsigned char data[]; /* sound data */
};
struct sa_stream
{
int audio_fd;
int audio_fd;
pthread_mutex_t mutex;
pthread_t thread_id;
int playing;
int64_t bytes_played;
/* audio format info */
/* default setting */
@ -65,10 +76,21 @@ struct sa_stream
unsigned int rate;
unsigned int n_channels;
unsigned int precision;
int64_t bytes_played;
/* buffer list */
sa_buf *bl_head;
sa_buf *bl_tail;
};
/* Use a default buffer size with enough room for one second of audio,
* assuming stereo data at 44.1kHz with 32 bits per channel, and impose
* a generous limit on the number of buffers.
*/
#define BUF_SIZE (2 * 44100 * 4)
static void* audio_callback(void* s);
static sa_buf *new_buffer(int size);
/*
* -----------------------------------------------------------------------------
@ -106,12 +128,19 @@ sa_stream_create_pcm(
if ((s = malloc(sizeof(sa_stream_t))) == NULL)
return SA_ERROR_OOM;
s->audio_fd = NULL;
if (pthread_mutex_init(&s->mutex, NULL) != 0) {
free(s);
return SA_ERROR_SYSTEM;
}
s->audio_fd = NULL;
s->rate = rate;
s->n_channels = n_channels;
s->precision = 16;
s->playing = 0;
s->bytes_played = 0;
s->bl_tail = s->bl_head = NULL;
*_s = s;
@ -197,13 +226,19 @@ sa_stream_open(sa_stream_t *s)
int
sa_stream_destroy(sa_stream_t *s)
{
int result = SA_SUCCESS;
int result;
if (s == NULL)
return SA_SUCCESS;
pthread_mutex_lock(&s->mutex);
result = SA_SUCCESS;
/*
* Shut down the audio output device.
* and release resources
*/
if (s->audio_fd != NULL)
{
@ -214,6 +249,20 @@ sa_stream_destroy(sa_stream_t *s)
}
}
s->thread_id = 0;
while (s->bl_head != NULL) {
sa_buf * next = s->bl_head->next;
free(s->bl_head);
s->bl_head = next;
}
pthread_mutex_unlock(&s->mutex);
if (pthread_mutex_destroy(&s->mutex) != 0) {
result = SA_ERROR_SYSTEM;
}
free(s);
return result;
@ -229,12 +278,8 @@ int
sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes)
{
int result = SA_SUCCESS;
int total = 0;
int bytes = 0;
int fd;
int i;
audio_info_t ainfo;
int result;
sa_buf *buf;
if (s == NULL || s->audio_fd == NULL)
return SA_ERROR_NO_INIT;
@ -242,23 +287,83 @@ sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes)
if (nbytes == 0)
return SA_SUCCESS;
fd = s->audio_fd;
while (total < nbytes )
{
LOOP_WHILE_EINTR(bytes,(write(fd, (void *)(((unsigned char *)data) total), nbytes-total)));
/*
* Append the new data to the end of our buffer list.
*/
result = SA_SUCCESS;
buf = new_buffer(nbytes);
memcpy(buf->data,data, nbytes);
total = bytes;
if (total != nbytes)
printf("SunAudio\tWrite completed short - %d vs %d. Write more data\n",total,nbytes);
}
if ( buf == NULL)
return SA_ERROR_OOM;
s->bytes_played += nbytes;
pthread_mutex_lock(&s->mutex);
if (!s->bl_head)
s->bl_head = buf;
else
s->bl_tail->next = buf;
s->bl_tail = buf;
pthread_mutex_unlock(&s->mutex);
/*
* Once we have our first block of audio data, enable the audio callback
* function. This doesn't need to be protected by the mutex, because
* s->playing is not used in the audio callback thread, and it's probably
* better not to be inside the lock when we enable the audio callback.
*/
if (!s->playing) {
s->playing = 1;
if (pthread_create(&s->thread_id, NULL, audio_callback, s) != 0) {
result = SA_ERROR_SYSTEM;
}
}
return result;
}
static void*
audio_callback(void* data)
{
sa_stream_t* s = (sa_stream_t*)data;
sa_buf *buf;
int fd,nbytes_written,bytes,nbytes;
fd = s->audio_fd;
while (1)
{
if (s->thread_id == 0)
break;
pthread_mutex_lock(&s->mutex);
while (s->bl_head)
{
buf = s->bl_head;
s->bl_head = s->bl_head->next;
nbytes_written = 0;
nbytes = buf->size;
while (nbytes_written < nbytes)
{
LOOP_WHILE_EINTR(bytes,(write(fd, (void *)((buf->data)+nbytes_written), nbytes-nbytes_written)));
nbytes_written += bytes;
if (nbytes_written != nbytes)
printf("SunAudio\tWrite completed short - %d vs %d. Write more data\n",nbytes_written,nbytes);
}
free(buf);
s->bytes_played += nbytes;
}
pthread_mutex_unlock(&s->mutex);
}
return NULL;
}
/*
* -----------------------------------------------------------------------------
@ -267,19 +372,55 @@ sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes)
*/
int
sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos)
sa_stream_get_write_size(sa_stream_t *s, size_t *size)
{
sa_buf * b;
size_t used = 0;
if (s == NULL || s->audio_fd == NULL)
if (s == NULL )
return SA_ERROR_NO_INIT;
if (position != SA_POSITION_WRITE_SOFTWARE)
return SA_ERROR_NOT_SUPPORTED;
/* there is no interface to get the avaiable writing buffer size
* in sun audio, we return max size here to force sa_stream_write() to
* be called when there is data to be played
*/
*size = BUF_SIZE;
*pos = s->bytes_played;
return SA_SUCCESS;
}
/* ---------------------------------------------------------------------------
* General query and support functions
* -----------------------------------------------------------------------------
*/
int
sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos)
{
if (s == NULL) {
return SA_ERROR_NO_INIT;
}
if (position != SA_POSITION_WRITE_SOFTWARE) {
return SA_ERROR_NOT_SUPPORTED;
}
pthread_mutex_lock(&s->mutex);
*pos = s->bytes_played;
pthread_mutex_unlock(&s->mutex);
return SA_SUCCESS;
}
static sa_buf *
new_buffer(int size)
{
sa_buf * b = malloc(sizeof(sa_buf) + size);
if (b != NULL) {
b->size = size;
b->next = NULL;
}
return b;
}
/*
* -----------------------------------------------------------------------------
* Extension functions
@ -294,7 +435,7 @@ sa_stream_set_volume_abs(sa_stream_t *s, float vol)
audio_info_t audio_info;
newVolume = (AUDIO_MAX_GAIN-AUDIO_MIN_GAIN)*volAUDIO_MIN_GAIN;
newVolume = (AUDIO_MAX_GAIN-AUDIO_MIN_GAIN)*vol+AUDIO_MIN_GAIN;
/* Check if the new volume is valid or not */
if ( newVolume < AUDIO_MIN_GAIN || newVolume > AUDIO_MAX_GAIN )

View File

@ -223,6 +223,8 @@ int sa_stream_destroy(sa_stream_t *s) {
/* close and release all allocated resources */
status = closeAudio(s);
free(s);
return status;
}