summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--common.c585
-rw-r--r--common.h3
-rw-r--r--macro.h23
-rw-r--r--mutex.c86
-rw-r--r--mutex.h19
-rw-r--r--once.c58
-rw-r--r--once.h17
-rw-r--r--sydney.h19
-rw-r--r--thread.c200
-rw-r--r--thread.h27
11 files changed, 881 insertions, 160 deletions
diff --git a/Makefile b/Makefile
index 2064a0b..cd6deb4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
-CFLAGS=-Wall -O0 -g -W -Wno-unused-parameter `pkg-config --cflags liboil-0.3` -DRANDOM_PREFIX=sa -DOUTSIDE_SPEEX -D_GNU_SOURCE
+CFLAGS=-Wall -O0 -g -W -Wno-unused-parameter `pkg-config --cflags liboil-0.3` -DRANDOM_PREFIX=sa -DOUTSIDE_SPEEX -D_GNU_SOURCE -pthread
LIBS=-lm `pkg-config --libs liboil-0.3`
-SOURCES=common.c malloc.c test-sine.c oss.c bbuffer.c format.c volscale.c byteswap.c continued-fraction.c zero.c add.c speex/resample.c resample.c interleave.c converter.c g711.c
+SOURCES=common.c malloc.c test-sine.c oss.c bbuffer.c format.c volscale.c byteswap.c continued-fraction.c zero.c add.c speex/resample.c resample.c interleave.c converter.c g711.c mutex.c once.c thread.c
OBJS=$(SOURCES:.c=.o)
all: test-bufferq test-llist test-sine
diff --git a/common.c b/common.c
index dfdde95..a08316c 100644
--- a/common.c
+++ b/common.c
@@ -7,6 +7,7 @@
#include "malloc.h"
#include "common.h"
#include "driver.h"
+#include "mutex.h"
/* contains code */
#include "meta-name-table.h"
@@ -19,6 +20,11 @@ static sa_stream_t *stream_alloc(void) {
/* All fields a carefully chosen in a way that initializing them
* NUL bytes is sufficient */
+
+ if (!(d->mutex = sa_mutex_new(0))) {
+ sa_free(d);
+ return NULL;
+ }
return d;
}
@@ -48,7 +54,7 @@ int sa_stream_create_opaque(
oil_init();
if (client_name)
- if ((error = sa_stream_change_meta_data(*s, SA_META_CLIENT_NAME, client_name, strlen(client_name))) < 0)
+ if ((error = sa_stream_change_meta_data(*s, SA_META_CLIENT_NAME, client_name, strlen(client_name)+1)) < 0)
goto fail;
return SA_SUCCESS;
@@ -136,16 +142,19 @@ int sa_stream_open(sa_stream_t *s) {
int ret;
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE);
- sa_return_val_if_fail(s->codec || s->pcm_attrs.channel_map, SA_ERROR_NO_INIT);
- sa_return_val_if_fail(!(s->mode & SA_MODE_RDONLY) || (s->read_lower_watermark <= s->read_upper_watermark), SA_ERROR_INVALID);
- sa_return_val_if_fail(!(s->mode & SA_MODE_WRONLY) || (s->write_lower_watermark <= s->write_upper_watermark), SA_ERROR_INVALID);
- sa_return_val_if_fail(!(s->mode & SA_MODE_RDONLY) || !s->codec || (s->read_lower_watermark > 0 && s->read_upper_watermark > 0), SA_ERROR_NO_INIT);
- sa_return_val_if_fail(!(s->mode & SA_MODE_WRONLY) || !s->codec || (s->write_lower_watermark > 0 && s->write_upper_watermark > 0), SA_ERROR_NO_INIT);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->codec || s->pcm_attrs.channel_map, SA_ERROR_NO_INIT);
+ sa_return_val_if_fail_mutex(s->mutex, !(s->mode & SA_MODE_RDONLY) || (s->read_lower_watermark <= s->read_upper_watermark), SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, !(s->mode & SA_MODE_WRONLY) || (s->write_lower_watermark <= s->write_upper_watermark), SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, !(s->mode & SA_MODE_RDONLY) || !s->codec || (s->read_lower_watermark > 0 && s->read_upper_watermark > 0), SA_ERROR_NO_INIT);
+ sa_return_val_if_fail_mutex(s->mutex, !(s->mode & SA_MODE_WRONLY) || !s->codec || (s->write_lower_watermark > 0 && s->write_upper_watermark > 0), SA_ERROR_NO_INIT);
if ((ret = driver_open(s)) == 0)
s->state = SA_STATE_STOPPED;
+ sa_mutex_unlock(s->mutex);
+
return ret;
}
@@ -153,7 +162,7 @@ int sa_stream_destroy(sa_stream_t *s) {
int ret;
unsigned u;
- sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s, SA_ERROR_INVALID);
ret = driver_destroy(s);
@@ -167,6 +176,7 @@ int sa_stream_destroy(sa_stream_t *s) {
for (u = 0; u < _META_NAMES_MAX; u++)
sa_free(s->meta_data[u]);
+ sa_mutex_free(s->mutex);
sa_free(s);
return ret;
}
@@ -175,10 +185,14 @@ int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(size > 0, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE);
- sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
s->write_lower_watermark = size;
+
+ sa_mutex_unlock(s->mutex);
+
return SA_SUCCESS;
}
@@ -186,10 +200,13 @@ int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(size > 0, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE);
- sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
s->read_lower_watermark = size;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
@@ -197,10 +214,13 @@ int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(size > 0, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE);
- sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
s->write_upper_watermark = size;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
@@ -208,10 +228,13 @@ int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(size > 0, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE);
- sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
s->read_upper_watermark = size;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
@@ -221,47 +244,62 @@ int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t *map, unsigned
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(map, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
- sa_return_val_if_fail(n == s->pcm_attrs.nchannels, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, n == s->pcm_attrs.nchannels, SA_ERROR_INVALID);
for (c = map; n > 0; c++, n--)
- if (*c >= _SA_CHANNEL_MAX)
+ if (*c >= _SA_CHANNEL_MAX) {
+ sa_mutex_unlock(s->mutex);
return SA_ERROR_INVALID;
+ }
- if (!(m = sa_memdup(map, sizeof(sa_channel_t) * s->pcm_attrs.nchannels)))
+ if (!(m = sa_memdup(map, sizeof(sa_channel_t) * s->pcm_attrs.nchannels))) {
+ sa_mutex_unlock(s->mutex);
return SA_ERROR_OOM;
+ }
sa_free(s->pcm_attrs.channel_map);
s->pcm_attrs.channel_map = m;
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(mode == SA_XRUN_MODE_STOP || mode == SA_XRUN_MODE_SPIN, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
s->xrun_mode = mode;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_set_non_interleaved(sa_stream_t *s, int enable) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
s->ni_enabled = !!enable;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
s->dynamic_rate_enabled = !!enable;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
@@ -270,23 +308,33 @@ int sa_stream_set_driver(sa_stream_t *s, const char *driver) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(driver, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
- if (!(d = sa_strdup(driver)))
+ if (!(d = sa_strdup(driver))) {
+ sa_mutex_unlock(s->mutex);
return SA_ERROR_OOM;
+ }
sa_free(s->driver);
s->driver = d;
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t *callback) {
+ int r;
+
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(callback, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
- return driver_start_thread(s, callback);
+ r = driver_start_thread(s, callback);
+
+ sa_mutex_unlock(s->mutex);
+ return r;
}
int sa_stream_change_device(sa_stream_t *s, const char *device_name) {
@@ -299,6 +347,8 @@ int sa_stream_change_device(sa_stream_t *s, const char *device_name) {
if (!(d = sa_strdup(device_name)))
return SA_ERROR_OOM;
+ sa_mutex_lock(s->mutex);
+
ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_device(s, device_name);
if (ret == SA_SUCCESS) {
@@ -307,6 +357,8 @@ int sa_stream_change_device(sa_stream_t *s, const char *device_name) {
} else
sa_free(d);
+ sa_mutex_unlock(s->mutex);
+
return ret;
}
@@ -315,17 +367,22 @@ int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned n
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(vol, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
- sa_return_val_if_fail((!s->codec && n == s->pcm_attrs.nchannels) || s->pcm_attrs.nchannels == 1, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, (!s->codec && n == s->pcm_attrs.nchannels) || s->pcm_attrs.nchannels == 1, SA_ERROR_INVALID);
if (s->codec || s->pcm_attrs.nchannels == n) {
- if (!(v = sa_newdup(int32_t, vol, n)))
+ if (!(v = sa_newdup(int32_t, vol, n))) {
+ sa_mutex_unlock(s->mutex);
return SA_ERROR_OOM;
+ }
} else {
unsigned i;
- if (!(v = sa_new(int32_t, s->pcm_attrs.nchannels)))
+ if (!(v = sa_new(int32_t, s->pcm_attrs.nchannels))) {
+ sa_mutex_unlock(s->mutex);
return SA_ERROR_OOM;
+ }
for (i = 0; i < s->pcm_attrs.nchannels; i++)
v[i] = vol[0];
@@ -339,6 +396,7 @@ int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned n
} else
sa_free(v);
+ sa_mutex_unlock(s->mutex);
return ret;
}
@@ -347,17 +405,22 @@ int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(vol, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
- sa_return_val_if_fail((!s->codec && n == s->pcm_attrs.nchannels) || s->pcm_attrs.nchannels == 1, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, (!s->codec && n == s->pcm_attrs.nchannels) || s->pcm_attrs.nchannels == 1, SA_ERROR_INVALID);
if (s->codec || s->pcm_attrs.nchannels == n) {
- if (!(v = sa_newdup(int32_t, vol, n)))
+ if (!(v = sa_newdup(int32_t, vol, n))) {
+ sa_mutex_unlock(s->mutex);
return SA_ERROR_OOM;
+ }
} else {
unsigned i;
- if (!(v = sa_new(int32_t, s->pcm_attrs.nchannels)))
+ if (!(v = sa_new(int32_t, s->pcm_attrs.nchannels))) {
+ sa_mutex_unlock(s->mutex);
return SA_ERROR_OOM;
+ }
for (i = 0; i < s->pcm_attrs.nchannels; i++)
v[i] = vol[0];
@@ -371,6 +434,7 @@ int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned
} else
sa_free(v);
+ sa_mutex_unlock(s->mutex);
return ret;
}
@@ -379,285 +443,434 @@ int sa_stream_change_rate(sa_stream_t *s, unsigned rate) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(rate > 0, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
- sa_return_val_if_fail(s->dynamic_rate_enabled || s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->dynamic_rate_enabled || s->state == SA_STATE_INIT, SA_ERROR_STATE);
ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_rate(s, rate);
if (ret == SA_SUCCESS)
s->pcm_attrs.rate = rate;
+ sa_mutex_unlock(s->mutex);
return ret;
}
-int sa_stream_change_user_data(sa_stream_t *s, void *value) {
- sa_return_val_if_fail(s, SA_ERROR_INVALID);
-
- s->user_data = value;
+int sa_stream_change_user_data(sa_stream_t *s, const void *value) {
+ sa_return_val_if_fail(s->mutex, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+
+ s->user_data = (void*) value;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
s->adjust_rate = direction;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
s->adjust_rate = direction;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
s->adjust_pcm_format = direction;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
s->adjust_watermarks = direction;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_state(sa_stream_t *s, sa_state_t *state) {
+ int ret;
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(state, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, state, SA_ERROR_INVALID);
+
+ ret = driver_get_state(s, state);
- return driver_get_state(s, state);
+ sa_mutex_unlock(s->mutex);
+ return ret;
}
int sa_stream_get_rate(sa_stream_t *s, unsigned *rate) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(rate, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
*rate = s->pcm_attrs.rate;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(nchannels, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
*nchannels = s->pcm_attrs.nchannels;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *pcm_format) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(pcm_format, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
*pcm_format = s->pcm_attrs.format;
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(access_mode, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
*access_mode = s->mode;
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
-int sa_stream_get_codec(sa_stream_t *s, const char **codec) {
+int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size) {
+ size_t n;
+
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(codec, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail(size && (*size == 0 || codec), SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->codec, SA_ERROR_STATE);
+
+ n = strlen(s->codec)+1;
+ if (*size < n) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_SPACE;
+ }
- *codec = s->codec;
+ if (codec)
+ strcpy(codec, s->codec);
+ *size = n;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(size, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
*size = s->write_lower_watermark;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(size, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
*size = s->read_lower_watermark;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(size, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
*size = s->write_upper_watermark;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(size, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
*size = s->read_upper_watermark;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
-int sa_stream_get_channel_map(sa_stream_t *s, const sa_channel_t *map[]) {
+int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t *map, unsigned *n) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(map, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail(n && (*n == 0 || map), SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+
+ if (*n < s->pcm_attrs.nchannels) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_SPACE;
+ }
- *map = s->pcm_attrs.channel_map;
+ if (map)
+ memcpy(map, s->pcm_attrs.channel_map, s->pcm_attrs.nchannels * sizeof(sa_channel_t));
+ *n = s->pcm_attrs.nchannels;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(mode, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
*mode = s->xrun_mode;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(enabled, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
*enabled = s->ni_enabled;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(enabled, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
- *enabled = s->ni_enabled;
+ *enabled = s->dynamic_rate_enabled;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
-int sa_stream_get_driver(sa_stream_t *s, const char **driver) {
+int sa_stream_get_driver(sa_stream_t *s, char *driver, size_t *size) {
+ size_t n;
+
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(driver, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->driver, SA_ERROR_STATE);
+ sa_return_val_if_fail(size && (*size == 0 || driver), SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->driver, SA_ERROR_STATE);
+
+ n = strlen(s->driver)+1;
+ if (*size < n) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_SPACE;
+ }
- *driver = s->driver;
+ if (driver)
+ strcpy(driver, s->driver);
+ *size = n;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
-int sa_stream_get_device(sa_stream_t *s, const char **device_name) {
+int sa_stream_get_device(sa_stream_t *s, char *device, size_t *size) {
+ size_t n;
+
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(device_name, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->device, SA_ERROR_STATE);
+ sa_return_val_if_fail(size && (*size == 0 || device), SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->device, SA_ERROR_STATE);
+
+ n = strlen(s->device)+1;
+ if (*size < n) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_SPACE;
+ }
- *device_name = s->device;
+ if (device)
+ strcpy(device, s->device);
+ *size = n;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
-int sa_stream_get_read_volume(sa_stream_t *s, const int32_t *vol[]) {
+int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned *n) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(vol, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
- sa_return_val_if_fail(s->read_volume, SA_ERROR_STATE);
+ sa_return_val_if_fail(n && (*n == 0 || vol), SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->read_volume, SA_ERROR_STATE);
+
+ if (*n < s->pcm_attrs.nchannels) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_SPACE;
+ }
+
+ if (vol)
+ memcpy(vol, s->read_volume, s->pcm_attrs.nchannels * sizeof(int32_t));
+ *n = s->pcm_attrs.nchannels;
- *vol = s->read_volume;
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
-int sa_stream_get_write_volume(sa_stream_t *s, const int32_t *vol[]) {
+int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned *n) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(vol, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
- sa_return_val_if_fail(s->write_volume, SA_ERROR_STATE);
+ sa_return_val_if_fail(n && (*n == 0 || vol), SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->write_volume, SA_ERROR_STATE);
+
+ if (*n < s->pcm_attrs.nchannels) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_SPACE;
+ }
+
+ if (vol)
+ memcpy(vol, s->write_volume, s->pcm_attrs.nchannels * sizeof(int32_t));
+ *n = s->pcm_attrs.nchannels;
- *vol = s->write_volume;
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(direction, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
*direction = s->adjust_rate;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(direction, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
*direction = s->adjust_nchannels;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(direction, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
*direction = s->adjust_pcm_format;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction) {
- sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(direction, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, direction, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
*direction = s->adjust_watermarks;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_user_data(sa_stream_t *s, void **value) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(value, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
*value = s->user_data;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(error, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->event == SA_EVENT_ERROR, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->event == SA_EVENT_ERROR, SA_ERROR_STATE);
*error = s->error;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify) {
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(notify, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->event == SA_EVENT_NOTIFY, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->event == SA_EVENT_NOTIFY, SA_ERROR_STATE);
*notify = s->notify;
+
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
int sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
+ int ret;
+
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(pos, SA_ERROR_INVALID);
sa_return_val_if_fail(position < _SA_POSITION_MAX, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_get_position(s, position, pos);
- return driver_get_position(s, position, pos);
+ sa_mutex_unlock(s->mutex);
+ return ret;
}
int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes) {
@@ -677,103 +890,157 @@ int sa_stream_write_ni(sa_stream_t *s, unsigned channel, const void *data, size_
}
int sa_stream_pread(sa_stream_t *s, void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ int ret;
+
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(data, SA_ERROR_INVALID);
sa_return_val_if_fail(nbytes > 0, SA_ERROR_INVALID);
sa_return_val_if_fail(whence == SA_SEEK_RELATIVE || whence == SA_SEEK_ABSOLUTE || whence == SA_SEEK_RELATIVE_END, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->ni_enabled, SA_ERROR_STATE);
- sa_return_val_if_fail(s->codec || (nbytes % s->pcm_frame_size) == 0, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->codec || (offset % s->pcm_frame_size) == 0, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
- sa_return_val_if_fail(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->ni_enabled, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->codec || (nbytes % s->pcm_frame_size) == 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->codec || (offset % s->pcm_frame_size) == 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
- return driver_pread(s, data, nbytes, offset, whence);
+ ret = driver_pread(s, data, nbytes, offset, whence);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
}
int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ int ret;
+
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(data, SA_ERROR_INVALID);
sa_return_val_if_fail(nbytes > 0, SA_ERROR_INVALID);
sa_return_val_if_fail(whence == SA_SEEK_RELATIVE || whence == SA_SEEK_ABSOLUTE || whence == SA_SEEK_RELATIVE_END, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->ni_enabled, SA_ERROR_STATE);
- sa_return_val_if_fail(s->codec || (nbytes % s->pcm_frame_size) == 0, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->codec || (offset % s->pcm_frame_size) == 0, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
- sa_return_val_if_fail(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->ni_enabled, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->codec || (nbytes % s->pcm_frame_size) == 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->codec || (offset % s->pcm_frame_size) == 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
- return driver_pwrite(s, data, nbytes, offset, whence);
+ ret = driver_pwrite(s, data, nbytes, offset, whence);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
}
int sa_stream_pread_ni(sa_stream_t *s, unsigned channel, void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ int ret;
+
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(data, SA_ERROR_INVALID);
sa_return_val_if_fail(nbytes > 0, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
sa_return_val_if_fail(whence == SA_SEEK_RELATIVE || whence == SA_SEEK_ABSOLUTE || whence == SA_SEEK_RELATIVE_END, SA_ERROR_INVALID);
- sa_return_val_if_fail(channel < s->pcm_attrs.nchannels, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->ni_enabled, SA_ERROR_STATE);
- sa_return_val_if_fail((nbytes % s->pcm_sample_size) == 0, SA_ERROR_INVALID);
- sa_return_val_if_fail((offset % s->pcm_sample_size) == 0, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
- sa_return_val_if_fail(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
-
- return driver_pread_ni(s, channel, data, nbytes, offset, whence);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, channel < s->pcm_attrs.nchannels, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->ni_enabled, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, (nbytes % s->pcm_sample_size) == 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, (offset % s->pcm_sample_size) == 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_pread_ni(s, channel, data, nbytes, offset, whence);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
}
int sa_stream_pwrite_ni(sa_stream_t *s, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ int ret;
+
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(data, SA_ERROR_INVALID);
sa_return_val_if_fail(nbytes > 0, SA_ERROR_INVALID);
- sa_return_val_if_fail(!s->codec, SA_ERROR_STATE);
sa_return_val_if_fail(whence == SA_SEEK_RELATIVE || whence == SA_SEEK_ABSOLUTE || whence == SA_SEEK_RELATIVE_END, SA_ERROR_INVALID);
- sa_return_val_if_fail(channel < s->pcm_attrs.nchannels, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->ni_enabled, SA_ERROR_STATE);
- sa_return_val_if_fail((nbytes % s->pcm_sample_size) == 0, SA_ERROR_INVALID);
- sa_return_val_if_fail((offset % s->pcm_sample_size) == 0, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
- sa_return_val_if_fail(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
-
- return driver_pwrite_ni(s, channel, data, nbytes, offset, whence);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, channel < s->pcm_attrs.nchannels, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->ni_enabled, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, (nbytes % s->pcm_sample_size) == 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, (offset % s->pcm_sample_size) == 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_pwrite_ni(s, channel, data, nbytes, offset, whence);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
}
int sa_stream_get_read_size(sa_stream_t *s, size_t *size) {
+ int ret;
+
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(size, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
- sa_return_val_if_fail(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
- return driver_get_read_size(s, size);
+ ret = driver_get_read_size(s, size);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
}
int sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
+ int ret;
+
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(size, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
- sa_return_val_if_fail(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
- return driver_get_write_size(s, size);
+ ret = driver_get_write_size(s, size);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
}
int sa_stream_resume(sa_stream_t *s) {
+ int ret;
+
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
- return driver_resume(s);
+ ret = driver_resume(s);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
}
int sa_stream_pause(sa_stream_t *s) {
+ int ret;
+
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_pause(s);
- return driver_pause(s);
+ sa_mutex_unlock(s->mutex);
+ return ret;
}
int sa_stream_drain(sa_stream_t *s) {
+ int ret;
+
sa_return_val_if_fail(s, SA_ERROR_INVALID);
- sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
- sa_return_val_if_fail(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_drain(s);
- return driver_drain(s);
+ sa_mutex_unlock(s->mutex);
+ return ret;
}
size_t get_pcm_sample_size(sa_pcm_format_t f) {
@@ -899,17 +1166,24 @@ int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *dat
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(name, SA_ERROR_INVALID);
- sa_return_val_if_fail(data || size == 0, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, data || size == 0, SA_ERROR_INVALID);
- if (!(m = lookup_meta_name(name, strlen(name))))
+ if (!(m = lookup_meta_name(name, strlen(name)))) {
+ sa_mutex_unlock(s->mutex);
return SA_ERROR_NO_META;
+ }
- if (!check_table[m->idx](data, size))
+ if (!check_table[m->idx](data, size)) {
+ sa_mutex_unlock(s->mutex);
return SA_ERROR_INVALID;
+ }
if (data)
- if (!(d = sa_memdup(data, size)))
+ if (!(d = sa_memdup(data, size))) {
+ sa_mutex_unlock(s->mutex);
return SA_ERROR_OOM;
+ }
ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_meta_data(s, name, data, size);
@@ -920,26 +1194,39 @@ int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *dat
} else
sa_free(d);
+ sa_mutex_unlock(s->mutex);
return ret;
}
-int sa_stream_get_meta_data(sa_stream_t *s, const char *name, const void **data, size_t *size) {
+int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void *data, size_t *size) {
const struct meta_name *m;
sa_return_val_if_fail(s, SA_ERROR_INVALID);
sa_return_val_if_fail(name, SA_ERROR_INVALID);
- sa_return_val_if_fail(data, SA_ERROR_INVALID);
- sa_return_val_if_fail(size, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size && (*size == 0 || data), SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
- if (!(m = lookup_meta_name(name, strlen(name))))
+ if (!(m = lookup_meta_name(name, strlen(name)))) {
+ sa_mutex_unlock(s->mutex);
return SA_ERROR_NO_META;
+ }
- if (!s->meta_data[m->idx])
+ if (!s->meta_data[m->idx]) {
+ sa_mutex_unlock(s->mutex);
return SA_ERROR_NO_DATA;
+ }
+
+ if (*size < s->meta_data_size[m->idx]) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_SPACE;
+ }
+
+ if (data)
+ memcpy(data, s->meta_data[m->idx], s->meta_data_size[m->idx]);
- *data = s->meta_data[m->idx];
*size = s->meta_data_size[m->idx];
+ sa_mutex_unlock(s->mutex);
return SA_SUCCESS;
}
diff --git a/common.h b/common.h
index 41c43b1..58a9a3e 100644
--- a/common.h
+++ b/common.h
@@ -2,6 +2,7 @@
#define foocommonh
#include "sydney.h"
+#include "mutex.h"
#define _META_NAMES_MAX 9
@@ -55,6 +56,8 @@ struct sa_stream {
void *meta_data[_META_NAMES_MAX];
size_t meta_data_size[_META_NAMES_MAX];
+
+ sa_mutex_t *mutex;
};
size_t get_pcm_sample_size(sa_pcm_format_t f);
diff --git a/macro.h b/macro.h
index 847bf97..ed80654 100644
--- a/macro.h
+++ b/macro.h
@@ -26,10 +26,33 @@
} \
} while(0)
+#define sa_return_if_fail_mutex(m, expr) \
+ do { \
+ if (!(expr)) { \
+ fprintf(stderr, "%s: Assertion <%s> failed.\n", PRETTY_FUNCTION, #expr ); \
+ sa_mutex_unlock(m); \
+ return; \
+ } \
+ } while(0)
+
+#define sa_return_val_if_fail_mutex(m, expr, val) \
+ do { \
+ if (!(expr)) { \
+ fprintf(stderr, "%s: Assertion <%s> failed.\n", PRETTY_FUNCTION, #expr ); \
+ sa_mutex_unlock(m); \
+ return (val); \
+ } \
+ } while(0)
+
#define sa_assert assert
#define sa_assert_not_reached() sa_assert(!"Should not be reached.")
+#define sa_assert_success(x) do { \
+ int _r = (x); \
+ sa_assert(_r == 0); \
+ } while(0)
+
#define elementsof(x) (sizeof(x)/sizeof((x)[0]))
#ifndef MAX
diff --git a/mutex.c b/mutex.c
new file mode 100644
index 0000000..8674fff
--- /dev/null
+++ b/mutex.c
@@ -0,0 +1,86 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <pthread.h>
+
+#include "macro.h"
+#include "malloc.h"
+#include "mutex.h"
+
+struct sa_mutex {
+ pthread_mutex_t mutex;
+};
+
+struct sa_cond {
+ pthread_cond_t cond;
+};
+
+sa_mutex_t* sa_mutex_new(int recursive) {
+ sa_mutex_t *m;
+ pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init(&attr);
+
+ if (recursive)
+ sa_assert_success(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE));
+
+ if (!(m = sa_new(sa_mutex_t, 1)))
+ return NULL;
+
+ sa_assert_success(pthread_mutex_init(&m->mutex, &attr));
+ return m;
+}
+
+void sa_mutex_free(sa_mutex_t *m) {
+ assert(m);
+
+ sa_assert_success(pthread_mutex_destroy(&m->mutex));
+ sa_free(m);
+}
+
+void sa_mutex_lock(sa_mutex_t *m) {
+ assert(m);
+
+ sa_assert_success(pthread_mutex_lock(&m->mutex));
+}
+
+void sa_mutex_unlock(sa_mutex_t *m) {
+ assert(m);
+
+ sa_assert_success(pthread_mutex_unlock(&m->mutex));
+}
+
+sa_cond_t *sa_cond_new(void) {
+ sa_cond_t *c;
+
+ if (!(c = sa_new(sa_cond_t, 1)))
+ return NULL;
+
+ sa_assert_success(pthread_cond_init(&c->cond, NULL));
+ return c;
+}
+
+void sa_cond_free(sa_cond_t *c) {
+ assert(c);
+
+ sa_assert_success(pthread_cond_destroy(&c->cond));
+ sa_free(c);
+}
+
+void sa_cond_signal(sa_cond_t *c, int broadcast) {
+ assert(c);
+
+ if (broadcast)
+ sa_assert_success(pthread_cond_broadcast(&c->cond));
+ else
+ sa_assert_success(pthread_cond_signal(&c->cond));
+}
+
+int sa_cond_wait(sa_cond_t *c, sa_mutex_t *m) {
+ assert(c);
+ assert(m);
+
+ return pthread_cond_wait(&c->cond, &m->mutex);
+}
diff --git a/mutex.h b/mutex.h
new file mode 100644
index 0000000..e83feb8
--- /dev/null
+++ b/mutex.h
@@ -0,0 +1,19 @@
+#ifndef foosydneymutexhfoo
+#define foosydneymutexhfoo
+
+typedef struct sa_mutex sa_mutex_t;
+
+sa_mutex_t* sa_mutex_new(int recursive);
+void sa_mutex_free(sa_mutex_t *m);
+void sa_mutex_lock(sa_mutex_t *m);
+void sa_mutex_unlock(sa_mutex_t *m);
+int sa_mutex_try_lock(sa_mutex_t *m);
+
+typedef struct sa_cond sa_cond_t;
+
+sa_cond_t *sa_cond_new(void);
+void sa_cond_free(sa_cond_t *c);
+void sa_cond_signal(sa_cond_t *c, int broadcast);
+int sa_cond_wait(sa_cond_t *c, sa_mutex_t *m);
+
+#endif
diff --git a/once.c b/once.c
new file mode 100644
index 0000000..de41be3
--- /dev/null
+++ b/once.c
@@ -0,0 +1,58 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pthread.h>
+#include <assert.h>
+
+#include "once.h"
+#include "mutex.h"
+#include "malloc.h"
+#include "macro.h"
+
+static sa_mutex_t *global_mutex;
+static pthread_once_t global_mutex_once = PTHREAD_ONCE_INIT;
+
+static void global_mutex_once_func(void) {
+ global_mutex = sa_mutex_new(0);
+}
+
+int sa_once(sa_once_t *control, sa_once_func_t func) {
+ int r;
+
+ assert(control);
+ assert(func);
+
+ /* Create the global mutex */
+ sa_assert_success(pthread_once(&global_mutex_once, global_mutex_once_func));
+
+ if (!global_mutex)
+ return -1;
+
+ r = 0;
+
+ /* Create the local mutex */
+ sa_mutex_lock(global_mutex);
+ if (!control->mutex) {
+ if (!(control->mutex = sa_mutex_new(1)))
+ r = -1;
+ }
+ sa_mutex_unlock(global_mutex);
+
+ if (!r)
+ return -1;
+
+ /* Execute function */
+ sa_mutex_lock(control->mutex);
+ if (!control->once_value) {
+ control->once_value = 1;
+ func();
+ }
+ sa_mutex_unlock(control->mutex);
+
+ /* Caveat: We have to make sure that the once func has completed
+ * before returning, even if the once func is not actually
+ * executed by us. Hence the awkward locking. */
+
+ return 0;
+}
diff --git a/once.h b/once.h
new file mode 100644
index 0000000..a374529
--- /dev/null
+++ b/once.h
@@ -0,0 +1,17 @@
+#ifndef foosydneyoncehfoo
+#define foosydneyoncehfoo
+
+#include "mutex.h"
+
+typedef struct sa_once {
+ unsigned int once_value;
+ sa_mutex_t *mutex;
+} sa_once_t;
+
+#define SA_ONCE_INIT { .once_value = 0, .mutex = NULL }
+
+typedef void (*sa_once_func_t) (void);
+
+int sa_once(sa_once_t *o, sa_once_func_t f);
+
+#endif
diff --git a/sydney.h b/sydney.h
index 30afc03..e9ba724 100644
--- a/sydney.h
+++ b/sydney.h
@@ -106,7 +106,8 @@ typedef enum {
SA_ERROR_NO_INIT = -9,
SA_ERROR_NO_META = -10,
SA_ERROR_NO_DATA = -11,
- _SA_ERROR_MAX = -12
+ SA_ERROR_NO_SPACE = -12,
+ _SA_ERROR_MAX = -13
} sa_error_t;
/** Possible events for notifications */
@@ -287,7 +288,7 @@ int sa_stream_change_rate(sa_stream_t *s, unsigned rate);
int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size);
/** Associate opaque user data */
-int sa_stream_change_user_data(sa_stream_t *s, void *value);
+int sa_stream_change_user_data(sa_stream_t *s, const void *value);
/* Hardware-related. This is implementation-specific and hardware specific. */
int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction);
@@ -298,7 +299,7 @@ int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction);
/* Query functions */
int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode);
-int sa_stream_get_codec(sa_stream_t *s, const char **codec);
+int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size);
int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format);
int sa_stream_get_rate(sa_stream_t *s, unsigned *rate);
int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels);
@@ -307,15 +308,15 @@ int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size);
int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size);
int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size);
int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size);
-int sa_stream_get_channel_map(sa_stream_t *s, const sa_channel_t *map[]);
+int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned *n);
int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode);
int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled);
int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled);
-int sa_stream_get_driver(sa_stream_t *s, const char **driver);
-int sa_stream_get_device(sa_stream_t *s, const char **device_name);
-int sa_stream_get_read_volume(sa_stream_t *s, const int32_t *vol[]);
-int sa_stream_get_write_volume(sa_stream_t *s, const int32_t *vol[]);
-int sa_stream_get_meta_data(sa_stream_t *s, const char *name, const void **data, size_t *size);
+int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size);
+int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size);
+int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned *n);
+int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned *n);
+int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void *data, size_t *size);
int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction);
int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction);
int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction);
diff --git a/thread.c b/thread.c
new file mode 100644
index 0000000..806e210
--- /dev/null
+++ b/thread.c
@@ -0,0 +1,200 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pthread.h>
+#include <sched.h>
+#include <errno.h>
+
+#include "thread.h"
+#include "malloc.h"
+#include "once.h"
+#include "macro.h"
+
+struct sa_thread {
+ pthread_t id;
+ sa_thread_func_t thread_func;
+ void *userdata;
+ int running;
+ pthread_mutex_t running_mutex;
+};
+
+struct sa_tls {
+ pthread_key_t key;
+};
+
+static sa_tls_t *thread_tls;
+static sa_once_t thread_tls_once = SA_ONCE_INIT;
+
+static void tls_free_cb(void *p) {
+ sa_thread_t *t = p;
+
+ sa_assert(t);
+
+ if (!t->thread_func)
+ /* This is a foreign thread, we need to free the struct */
+ sa_free(t);
+}
+
+static void thread_tls_once_func(void) {
+ thread_tls = sa_tls_new(tls_free_cb);
+}
+
+static void* internal_thread_func(void *userdata) {
+ sa_thread_t *t = userdata;
+ sa_assert(t);
+
+ t->id = pthread_self();
+ sa_tls_set(thread_tls, t);
+
+ t->thread_func(t->userdata);
+
+ sa_assert_success(pthread_mutex_lock(&t->running_mutex));
+ t->running = 0;
+ sa_assert_success(pthread_mutex_unlock(&t->running_mutex));
+
+ return NULL;
+}
+
+sa_thread_t* sa_thread_new(sa_thread_func_t thread_func, void *userdata) {
+ sa_thread_t *t;
+
+ sa_assert(thread_func);
+
+ if (sa_once(&thread_tls_once, thread_tls_once_func) < 0 || !thread_tls)
+ return NULL;
+
+ if (!(t = sa_new(sa_thread_t, 1)))
+ return NULL;
+
+ t->thread_func = thread_func;
+ t->userdata = userdata;
+
+ sa_assert_success(pthread_mutex_init(&t->running_mutex, NULL));
+ t->running = 1;
+
+ if (pthread_create(&t->id, NULL, internal_thread_func, t) < 0) {
+ sa_assert_success(pthread_mutex_destroy(&t->running_mutex));
+ sa_free(t);
+ return NULL;
+ }
+
+ return t;
+}
+
+int sa_thread_is_running(sa_thread_t *t) {
+ int b;
+ sa_assert(t);
+
+ if (!t->thread_func) {
+ /* Mhmm, this is a foreign thread, t->running is not
+ * necessarily valid. We misuse pthread_getschedparam() to
+ * check if the thread is valid. This might not be portable. */
+
+ int policy;
+ struct sched_param param;
+
+ return pthread_getschedparam(t->id, &policy, &param) >= 0 || errno != ESRCH;
+ }
+
+ sa_assert_success(pthread_mutex_lock(&t->running_mutex));
+ b = t->running;
+ sa_assert_success(pthread_mutex_unlock(&t->running_mutex));
+
+ return !!b;
+}
+
+void sa_thread_free(sa_thread_t *t) {
+ sa_assert(t);
+
+ sa_thread_join(t);
+ sa_assert_success(pthread_mutex_destroy(&t->running_mutex));
+ sa_free(t);
+}
+
+int sa_thread_join(sa_thread_t *t) {
+ sa_assert(t);
+
+ return pthread_join(t->id, NULL);
+}
+
+sa_thread_t* sa_thread_self(void) {
+ sa_thread_t *t;
+
+ if (sa_once(&thread_tls_once, thread_tls_once_func) < 0 || !thread_tls)
+ return NULL;
+
+ if ((t = sa_tls_get(thread_tls)))
+ return t;
+
+ /* This is a foreign thread, let's create a pthread structure to
+ * make sure that we can always return a sensible pointer */
+
+ if (!(t = sa_new(sa_thread_t, 1)))
+ return NULL;
+
+ t->id = pthread_self();
+ t->thread_func = NULL;
+ t->userdata = NULL;
+ t->running = 1;
+
+ sa_tls_set(thread_tls, t);
+
+ return t;
+}
+
+void* sa_thread_get_data(sa_thread_t *t) {
+ sa_assert(t);
+
+ return t->userdata;
+}
+
+void sa_thread_set_data(sa_thread_t *t, void *userdata) {
+ sa_assert(t);
+
+ t->userdata = userdata;
+}
+
+void sa_thread_yield(void) {
+#ifdef HAVE_PTHREAD_YIELD
+ pthread_yield();
+#else
+ sa_assert_success(sched_yield());
+#endif
+}
+
+sa_tls_t* sa_tls_new(sa_free_func_t free_cb) {
+ sa_tls_t *t;
+
+ if (!(t = sa_new(sa_tls_t, 1)))
+ return NULL;
+
+ if (pthread_key_create(&t->key, free_cb) < 0) {
+ sa_free(t);
+ return NULL;
+ }
+
+ return t;
+}
+
+void sa_tls_free(sa_tls_t *t) {
+ sa_assert(t);
+
+ sa_assert_success(pthread_key_delete(t->key));
+ sa_free(t);
+}
+
+void *sa_tls_get(sa_tls_t *t) {
+ sa_assert(t);
+
+ return pthread_getspecific(t->key);
+}
+
+void *sa_tls_set(sa_tls_t *t, void *userdata) {
+ void *r;
+
+ r = pthread_getspecific(t->key);
+ sa_assert_success(pthread_setspecific(t->key, userdata));
+ return r;
+}
+
diff --git a/thread.h b/thread.h
new file mode 100644
index 0000000..1a61e7a
--- /dev/null
+++ b/thread.h
@@ -0,0 +1,27 @@
+#ifndef foosydneythreadhfoo
+#define foosydneythreadhfoo
+
+typedef struct sa_thread sa_thread_t;
+
+typedef void (*sa_thread_func_t) (void *userdata);
+
+sa_thread_t* sa_thread_new(sa_thread_func_t sa_thread_func, void *userdata);
+void sa_thread_free(sa_thread_t *t);
+int sa_thread_join(sa_thread_t *t);
+int sa_thread_is_running(sa_thread_t *t);
+sa_thread_t *sa_thread_self(void);
+void sa_thread_yield(void);
+
+void* sa_thread_get_data(sa_thread_t *t);
+void sa_thread_set_data(sa_thread_t *t, void *userdata);
+
+typedef struct sa_tls sa_tls_t;
+
+typedef void (*sa_free_func_t) (void *data);
+
+sa_tls_t* sa_tls_new(sa_free_func_t f);
+void sa_tls_free(sa_tls_t *t);
+void * sa_tls_get(sa_tls_t *t);
+void *sa_tls_set(sa_tls_t *t, void *userdata);
+
+#endif