ALSA could then leave much more CPU cycles for your applications, but you
could still need some floating point emulator.
+Thread-safety option
+--------------------
+
+As default, major PCM functions of alsa-lib are built to be
+thread-safe with pthread mutex (while this wasn't present in the
+versions earlier than 1.1.2). If you want to build without this
+thread-safety support but reduce the overhead, pass
+--disable-thread-safety configure option.
+
Jack plugin
-----------
fi
AC_DEFINE_UNQUOTED(SND_MAX_CARDS, $max_cards, [Max number of cards])
+dnl Check for thread-safe API functions
+if test "$HAVE_LIBPTHREAD" = "yes"; then
+AC_MSG_CHECKING(for thread-safe API functions)
+AC_ARG_ENABLE(thread-safety,
+ AS_HELP_STRING([--disable-thread-safety],
+ [disable thread-safe API functions]),
+ threadsafe="$enableval", threadsafe="yes")
+if test "$threadsafe" = "yes"; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE([THREAD_SAFE_API], "1", [Disable thread-safe API functions])
+else
+ AC_MSG_RESULT(no)
+fi
+fi
+
dnl Make a symlink for inclusion of alsa/xxx.h
if test ! -L "$srcdir"/include/alsa ; then
echo "Making a symlink include/alsa"
/** Callback table of ioplug */
struct snd_pcm_ioplug_callback {
/**
- * start the PCM; required
+ * start the PCM; required, called inside mutex lock
*/
int (*start)(snd_pcm_ioplug_t *io);
/**
- * stop the PCM; required
+ * stop the PCM; required, called inside mutex lock
*/
int (*stop)(snd_pcm_ioplug_t *io);
/**
- * get the current DMA position; required
+ * get the current DMA position; required, called inside mutex lock
*/
snd_pcm_sframes_t (*pointer)(snd_pcm_ioplug_t *io);
/**
- * transfer the data; optional
+ * transfer the data; optional, called inside mutex lock
*/
snd_pcm_sframes_t (*transfer)(snd_pcm_ioplug_t *io,
const snd_pcm_channel_area_t *areas,
*/
int (*drain)(snd_pcm_ioplug_t *io);
/**
- * toggle pause; optional
+ * toggle pause; optional, called inside mutex lock
*/
int (*pause)(snd_pcm_ioplug_t *io, int enable);
/**
function is called, all operations managing the stream state for these two
streams are joined. The opposite function is #snd_pcm_unlink().
+\section pcm_thread_safety Thread-safety
+
+When the library is configured with the proper option, some PCM functions
+(e.g. #snd_pcm_avail_update()) are thread-safe and can be called concurrently
+from multiple threads. Meanwhile, some functions (e.g. #snd_pcm_hw_params())
+aren't thread-safe, and application needs to call them carefully when they
+are called from multiple threads. In general, all the functions that are
+often called during streaming are covered as thread-safe.
+
\section pcm_dev_names PCM naming conventions
The ALSA library uses a generic string representation for names of devices.
* \param pcm PCM handle
* \param nonblock 0 = block, 1 = nonblock mode, 2 = abort
* \return 0 on success otherwise a negative error code
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_nonblock(snd_pcm_t *pcm, int nonblock)
{
- int err;
+ int err = 0;
+
assert(pcm);
+ __snd_pcm_lock(pcm); /* forced lock due to pcm field change */
if ((err = pcm->ops->nonblock(pcm->op_arg, nonblock)) < 0)
- return err;
+ goto unlock;
if (nonblock == 2) {
pcm->mode |= SND_PCM_ABORT;
- return 0;
+ goto unlock;
}
if (nonblock)
pcm->mode |= SND_PCM_NONBLOCK;
else {
if (pcm->hw_flags & SND_PCM_HW_PARAMS_NO_PERIOD_WAKEUP)
- return -EINVAL;
- pcm->mode &= ~SND_PCM_NONBLOCK;
+ err = -EINVAL;
+ else
+ pcm->mode &= ~SND_PCM_NONBLOCK;
}
- return 0;
+ unlock:
+ __snd_pcm_unlock(pcm);
+ return err;
}
#ifndef DOC_HIDDEN
* The software parameters can be changed at any time.
* The hardware parameters cannot be changed when the stream is
* running (active).
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
{
return -EINVAL;
}
#endif
+ __snd_pcm_lock(pcm); /* forced lock due to pcm field change */
err = pcm->ops->sw_params(pcm->op_arg, params);
- if (err < 0)
+ if (err < 0) {
+ __snd_pcm_unlock(pcm);
return err;
+ }
pcm->tstamp_mode = params->tstamp_mode;
pcm->tstamp_type = params->tstamp_type;
pcm->period_step = params->period_step;
pcm->silence_threshold = params->silence_threshold;
pcm->silence_size = params->silence_size;
pcm->boundary = params->boundary;
+ __snd_pcm_unlock(pcm);
return 0;
}
* \param pcm PCM handle
* \param status Status container
* \return 0 on success otherwise a negative error code
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
{
+ int err;
+
assert(pcm && status);
- return pcm->fast_ops->status(pcm->fast_op_arg, status);
+ snd_pcm_lock(pcm);
+ err = pcm->fast_ops->status(pcm->fast_op_arg, status);
+ snd_pcm_unlock(pcm);
}
/**
*
* This is a faster way to obtain only the PCM state without calling
* \link ::snd_pcm_status() \endlink.
+ *
+ * The function is thread-safe when built with the proper option.
*/
snd_pcm_state_t snd_pcm_state(snd_pcm_t *pcm)
{
+ snd_pcm_state_t state;
+
assert(pcm);
- return pcm->fast_ops->state(pcm->fast_op_arg);
+ snd_pcm_lock(pcm);
+ state = __snd_pcm_state(pcm);
+ snd_pcm_unlock(pcm);
+ return state;
}
/**
* Note this function does not update the actual r/w pointer
* for applications. The function #snd_pcm_avail_update()
* have to be called before any mmap begin+commit operation.
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_hwsync(snd_pcm_t *pcm)
{
+ int err;
+
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
- return pcm->fast_ops->hwsync(pcm->fast_op_arg);
+ snd_pcm_lock(pcm);
+ err = __snd_pcm_hwsync(pcm);
+ snd_pcm_unlock(pcm);
+ return err;
}
#ifndef DOC_HIDDEN
link_warning(snd_pcm_hwsync, "Warning: snd_pcm_hwsync() is deprecated, consider to use snd_pcm_avail()");
* Note this function does not update the actual r/w pointer
* for applications. The function #snd_pcm_avail_update()
* have to be called before any begin+commit operation.
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
{
+ int err;
+
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
- return pcm->fast_ops->delay(pcm->fast_op_arg, delayp);
+ snd_pcm_lock(pcm);
+ err = __snd_pcm_delay(pcm, delayp);
+ snd_pcm_unlock(pcm);
+ return err;
}
/**
* to do the fine resume from this state. Not all hardware supports
* this feature, when an -ENOSYS error is returned, use the \link ::snd_pcm_prepare() \endlink
* function to recovery.
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_resume(snd_pcm_t *pcm)
{
SNDMSG("PCM not set up");
return -EIO;
}
+ /* lock handled in the callback */
return pcm->fast_ops->resume(pcm->fast_op_arg);
}
*
* Note this function does not update the actual r/w pointer
* for applications.
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail, snd_htimestamp_t *tstamp)
{
+ int err;
+
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
- return pcm->fast_ops->htimestamp(pcm->fast_op_arg, avail, tstamp);
+ snd_pcm_lock(pcm);
+ err = pcm->fast_ops->htimestamp(pcm->fast_op_arg, avail, tstamp);
+ snd_pcm_unlock(pcm);
+ return err;
}
/**
* \brief Prepare PCM for use
* \param pcm PCM handle
* \return 0 on success otherwise a negative error code
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_prepare(snd_pcm_t *pcm)
{
+ int err;
+
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
- return pcm->fast_ops->prepare(pcm->fast_op_arg);
+ snd_pcm_lock(pcm);
+ err = pcm->fast_ops->prepare(pcm->fast_op_arg);
+ snd_pcm_unlock(pcm);
+ return err;
}
/**
* \return 0 on success otherwise a negative error code
*
* Reduce PCM delay to 0.
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_reset(snd_pcm_t *pcm)
{
+ int err;
+
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
- return pcm->fast_ops->reset(pcm->fast_op_arg);
+ snd_pcm_lock(pcm);
+ err = pcm->fast_ops->reset(pcm->fast_op_arg);
+ snd_pcm_unlock(pcm);
+ return err;
}
/**
* \brief Start a PCM
* \param pcm PCM handle
* \return 0 on success otherwise a negative error code
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_start(snd_pcm_t *pcm)
{
+ int err;
+
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
- return pcm->fast_ops->start(pcm->fast_op_arg);
+ snd_pcm_lock(pcm);
+ err = __snd_pcm_start(pcm);
+ snd_pcm_unlock(pcm);
+ return err;
}
/**
*
* For processing all pending samples, use \link ::snd_pcm_drain() \endlink
* instead.
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_drop(snd_pcm_t *pcm)
{
+ int err;
+
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
- return pcm->fast_ops->drop(pcm->fast_op_arg);
+ snd_pcm_lock(pcm);
+ err = pcm->fast_ops->drop(pcm->fast_op_arg);
+ snd_pcm_unlock(pcm);
+ return err;
}
/**
*
* For stopping the PCM stream immediately, use \link ::snd_pcm_drop() \endlink
* instead.
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_drain(snd_pcm_t *pcm)
{
SNDMSG("PCM not set up");
return -EIO;
}
+ /* lock handled in the callback */
return pcm->fast_ops->drain(pcm->fast_op_arg);
}
* Note that this function works only on the hardware which supports
* pause feature. You can check it via \link ::snd_pcm_hw_params_can_pause() \endlink
* function.
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_pause(snd_pcm_t *pcm, int enable)
{
+ int err;
+
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
- return pcm->fast_ops->pause(pcm->fast_op_arg, enable);
+ snd_pcm_lock(pcm);
+ err = pcm->fast_ops->pause(pcm->fast_op_arg, enable);
+ snd_pcm_unlock(pcm);
+ return err;
}
/**
* Note: The snd_pcm_rewind() can accept bigger value than returned
* by this function. But it is not guaranteed that output stream
* will be consistent with bigger value.
+ *
+ * The function is thread-safe when built with the proper option.
*/
snd_pcm_sframes_t snd_pcm_rewindable(snd_pcm_t *pcm)
{
+ snd_pcm_sframes_t result;
+
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
- return pcm->fast_ops->rewindable(pcm->fast_op_arg);
+ snd_pcm_lock(pcm);
+ result = pcm->fast_ops->rewindable(pcm->fast_op_arg);
+ snd_pcm_unlock(pcm);
+ return result;
}
/**
* \param frames wanted displacement in frames
* \return a positive number for actual displacement otherwise a
* negative error code
+ *
+ * The function is thread-safe when built with the proper option.
*/
snd_pcm_sframes_t snd_pcm_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
{
+ snd_pcm_sframes_t result;
+
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
}
if (frames == 0)
return 0;
- return pcm->fast_ops->rewind(pcm->fast_op_arg, frames);
+ snd_pcm_lock(pcm);
+ result = pcm->fast_ops->rewind(pcm->fast_op_arg, frames);
+ snd_pcm_unlock(pcm);
+ return result;
}
/**
* Note: The snd_pcm_forward() can accept bigger value than returned
* by this function. But it is not guaranteed that output stream
* will be consistent with bigger value.
+ *
+ * The function is thread-safe when built with the proper option.
*/
snd_pcm_sframes_t snd_pcm_forwardable(snd_pcm_t *pcm)
{
+ snd_pcm_sframes_t result;
+
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
- return pcm->fast_ops->forwardable(pcm->fast_op_arg);
+ snd_pcm_lock(pcm);
+ result = pcm->fast_ops->forwardable(pcm->fast_op_arg);
+ snd_pcm_unlock(pcm);
+ return result;
}
/**
* \param frames wanted skip in frames
* \return a positive number for actual skip otherwise a negative error code
* \retval 0 means no action
+ *
+ * The function is thread-safe when built with the proper option.
*/
#ifndef DOXYGEN
snd_pcm_sframes_t INTERNAL(snd_pcm_forward)(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
snd_pcm_sframes_t snd_pcm_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
#endif
{
+ snd_pcm_sframes_t result;
+
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
}
if (frames == 0)
return 0;
- return pcm->fast_ops->forward(pcm->fast_op_arg, frames);
+ snd_pcm_lock(pcm);
+ result = pcm->fast_ops->forward(pcm->fast_op_arg, frames);
+ snd_pcm_unlock(pcm);
+ return result;
}
use_default_symbol_version(__snd_pcm_forward, snd_pcm_forward, ALSA_0.9.0rc8);
* The returned number of frames can be less only if a signal or underrun occurred.
*
* If the non-blocking behaviour is selected, then routine doesn't wait at all.
+ *
+ * The function is thread-safe when built with the proper option.
*/
snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
* The returned number of frames can be less only if a signal or underrun occurred.
*
* If the non-blocking behaviour is selected, then routine doesn't wait at all.
+ *
+ * The function is thread-safe when built with the proper option.
*/
snd_pcm_sframes_t snd_pcm_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
{
* if a signal or underrun occurred.
*
* If the non-blocking behaviour is selected, then routine doesn't wait at all.
+ *
+ * The function is thread-safe when built with the proper option.
*/
snd_pcm_sframes_t snd_pcm_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
{
* if a signal or underrun occurred.
*
* If the non-blocking behaviour is selected, then routine doesn't wait at all.
+ *
+ * The function is thread-safe when built with the proper option.
*/
snd_pcm_sframes_t snd_pcm_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
{
return -ENOSYS;
}
+/* locked version */
+static int __snd_pcm_poll_descriptors_count(snd_pcm_t *pcm)
+{
+ if (pcm->fast_ops->poll_descriptors_count)
+ return pcm->fast_ops->poll_descriptors_count(pcm->fast_op_arg);
+ return pcm->poll_fd_count;
+}
+
/**
* \brief get count of poll descriptors for PCM handle
* \param pcm PCM handle
* \return count of poll descriptors
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_poll_descriptors_count(snd_pcm_t *pcm)
{
+ int count;
+
assert(pcm);
- if (pcm->fast_ops->poll_descriptors_count)
- return pcm->fast_ops->poll_descriptors_count(pcm->fast_op_arg);
- return pcm->poll_fd_count;
+ snd_pcm_lock(pcm);
+ count = __snd_pcm_poll_descriptors_count(pcm);
+ snd_pcm_unlock(pcm);
+ return count;
}
+/* locked version */
+static int __snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds,
+ unsigned int space)
+{
+ if (pcm->fast_ops->poll_descriptors)
+ return pcm->fast_ops->poll_descriptors(pcm->fast_op_arg, pfds, space);
+ if (pcm->poll_fd < 0) {
+ SNDMSG("poll_fd < 0");
+ return -EIO;
+ }
+ if (space >= 1 && pfds) {
+ pfds->fd = pcm->poll_fd;
+ pfds->events = pcm->poll_events | POLLERR | POLLNVAL;
+ } else {
+ return 0;
+ }
+ return 1;
+}
/**
* \brief get poll descriptors
* syscall, too. Do not forget to translate POLLIN and POLLOUT events to
* corresponding FD_SET arrays and demangle events using
* \link ::snd_pcm_poll_descriptors_revents() \endlink .
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
{
+ int err;
+
assert(pcm && pfds);
- if (pcm->fast_ops->poll_descriptors)
- return pcm->fast_ops->poll_descriptors(pcm->fast_op_arg, pfds, space);
- if (pcm->poll_fd < 0) {
- SNDMSG("poll_fd < 0");
- return -EIO;
- }
- if (space >= 1 && pfds) {
- pfds->fd = pcm->poll_fd;
- pfds->events = pcm->poll_events | POLLERR | POLLNVAL;
- } else {
- return 0;
- }
- return 1;
+ snd_pcm_lock(pcm);
+ err = __snd_pcm_poll_descriptors(pcm, pfds, space);
+ snd_pcm_unlock(pcm);
+ return err;
}
+static int __snd_pcm_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds,
+ unsigned int nfds, unsigned short *revents);
+
/**
* \brief get returned events from poll descriptors
* \param pcm PCM handle
*
* Note: Even if multiple poll descriptors are used (i.e. pfds > 1),
* this function returns only a single event.
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_poll_descriptors_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
{
+ int err;
+
assert(pcm && pfds && revents);
+ snd_pcm_lock(pcm);
+ err = __snd_pcm_poll_revents(pcm, pfds, nfds, revents);
+ snd_pcm_unlock(pcm);
+ return err;
+}
+
+static int __snd_pcm_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds,
+ unsigned int nfds, unsigned short *revents)
+{
if (pcm->fast_ops->poll_revents)
return pcm->fast_ops->poll_revents(pcm->fast_op_arg, pfds, nfds, revents);
if (nfds == 1) {
pcm->op_arg = pcm;
pcm->fast_op_arg = pcm;
INIT_LIST_HEAD(&pcm->async_handlers);
+#ifdef THREAD_SAFE_API
+ pthread_mutex_init(&pcm->lock, NULL);
+#endif
*pcmp = pcm;
return 0;
}
free(pcm->hw.link_dst);
free(pcm->appl.link_dst);
snd_dlobj_cache_put(pcm->open_func);
+#ifdef THREAD_SAFE_API
+ pthread_mutex_destroy(&pcm->lock);
+#endif
free(pcm);
return 0;
}
* others for general errors)
* \retval 0 timeout occurred
* \retval 1 PCM stream is ready for I/O
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_wait(snd_pcm_t *pcm, int timeout)
+{
+ int err;
+
+ __snd_pcm_lock(pcm); /* forced lock */
+ err = __snd_pcm_wait_in_lock(pcm, timeout);
+ __snd_pcm_unlock(pcm);
+ return err;
+}
+
+#ifndef DOC_HIDDEN
+/* locked version */
+int __snd_pcm_wait_in_lock(snd_pcm_t *pcm, int timeout)
{
if (!snd_pcm_may_wait_for_avail_min(pcm, snd_pcm_mmap_avail(pcm))) {
/* check more precisely */
- switch (snd_pcm_state(pcm)) {
+ switch (__snd_pcm_state(pcm)) {
case SND_PCM_STATE_XRUN:
return -EPIPE;
case SND_PCM_STATE_SUSPENDED:
return snd_pcm_wait_nocheck(pcm, timeout);
}
-#ifndef DOC_HIDDEN
/*
* like snd_pcm_wait() but doesn't check mmap_avail before calling poll()
*
* used in drain code in some plugins
+ *
+ * This function is called inside pcm lock.
*/
int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout)
{
unsigned short revents = 0;
int npfds, err, err_poll;
- npfds = snd_pcm_poll_descriptors_count(pcm);
+ npfds = __snd_pcm_poll_descriptors_count(pcm);
if (npfds <= 0 || npfds >= 16) {
SNDERR("Invalid poll_fds %d\n", npfds);
return -EIO;
}
pfd = alloca(sizeof(*pfd) * npfds);
- err = snd_pcm_poll_descriptors(pcm, pfd, npfds);
+ err = __snd_pcm_poll_descriptors(pcm, pfd, npfds);
if (err < 0)
return err;
if (err != npfds) {
return -EIO;
}
do {
+ __snd_pcm_unlock(pcm);
err_poll = poll(pfd, npfds, timeout);
+ __snd_pcm_lock(pcm);
if (err_poll < 0) {
if (errno == EINTR && !PCMINABORT(pcm))
continue;
}
if (! err_poll)
break;
- err = snd_pcm_poll_descriptors_revents(pcm, pfd, npfds, &revents);
+ err = __snd_pcm_poll_revents(pcm, pfd, npfds, &revents);
if (err < 0)
return err;
if (revents & (POLLERR | POLLNVAL)) {
/* check more precisely */
- switch (snd_pcm_state(pcm)) {
+ switch (__snd_pcm_state(pcm)) {
case SND_PCM_STATE_XRUN:
return -EPIPE;
case SND_PCM_STATE_SUSPENDED:
#if 0 /* very useful code to test poll related problems */
{
snd_pcm_sframes_t avail_update;
- snd_pcm_hwsync(pcm);
- avail_update = snd_pcm_avail_update(pcm);
+ __snd_pcm_hwsync(pcm);
+ avail_update = __snd_pcm_avail_update(pcm);
if (avail_update < (snd_pcm_sframes_t)pcm->avail_min) {
printf("*** snd_pcm_wait() FATAL ERROR!!!\n");
printf("avail_min = %li, avail_update = %li\n", pcm->avail_min, avail_update);
* Also this function might be called after #snd_pcm_delay() or
* #snd_pcm_hwsync() functions to move private ring buffer pointers
* in alsa-lib (the internal plugin chain).
+ *
+ * The function is thread-safe when built with the proper option.
*/
snd_pcm_sframes_t snd_pcm_avail_update(snd_pcm_t *pcm)
{
- return pcm->fast_ops->avail_update(pcm->fast_op_arg);
+ snd_pcm_sframes_t result;
+
+ snd_pcm_lock(pcm);
+ result = __snd_pcm_avail_update(pcm);
+ snd_pcm_unlock(pcm);
+ return result;
}
/**
*
* The position is synced with hardware (driver) position in the sound
* ring buffer in this functions.
+ *
+ * The function is thread-safe when built with the proper option.
*/
snd_pcm_sframes_t snd_pcm_avail(snd_pcm_t *pcm)
{
int err;
+ snd_pcm_sframes_t result;
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
- err = pcm->fast_ops->hwsync(pcm->fast_op_arg);
+ snd_pcm_lock(pcm);
+ err = __snd_pcm_hwsync(pcm);
if (err < 0)
- return err;
- return pcm->fast_ops->avail_update(pcm->fast_op_arg);
+ result = err;
+ else
+ result = __snd_pcm_avail_update(pcm);
+ snd_pcm_unlock(pcm);
+ return result;
}
/**
* \return zero on success otherwise a negative error code
*
* The avail and delay values retuned are in sync.
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_avail_delay(snd_pcm_t *pcm,
snd_pcm_sframes_t *availp,
SNDMSG("PCM not set up");
return -EIO;
}
- err = pcm->fast_ops->hwsync(pcm->fast_op_arg);
+ snd_pcm_lock(pcm);
+ err = __snd_pcm_hwsync(pcm);
if (err < 0)
- return err;
- sf = pcm->fast_ops->avail_update(pcm->fast_op_arg);
- if (sf < 0)
- return (int)sf;
- err = pcm->fast_ops->delay(pcm->fast_op_arg, delayp);
+ goto unlock;
+ sf = __snd_pcm_avail_update(pcm);
+ if (sf < 0) {
+ err = (int)sf;
+ goto unlock;
+ }
+ err = __snd_pcm_delay(pcm, delayp);
if (err < 0)
- return err;
+ goto unlock;
*availp = sf;
- return 0;
+ err = 0;
+ unlock:
+ snd_pcm_unlock(pcm);
+ return err;
}
/**
* \param pcm PCM handle
* \param params Software configuration container
* \return 0 on success otherwise a negative error code
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_sw_params_current(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
{
SNDMSG("PCM not set up");
return -EIO;
}
+ __snd_pcm_lock(pcm); /* forced lock due to pcm field changes */
params->proto = SNDRV_PCM_VERSION;
params->tstamp_mode = pcm->tstamp_mode;
params->tstamp_type = pcm->tstamp_type;
params->silence_threshold = pcm->silence_threshold;
params->silence_size = pcm->silence_size;
params->boundary = pcm->boundary;
+ __snd_pcm_unlock(pcm);
return 0;
}
*
* See the snd_pcm_mmap_commit() function to finish the frame processing in
* the direct areas.
+ *
+ * The function is thread-safe when built with the proper option.
*/
int snd_pcm_mmap_begin(snd_pcm_t *pcm,
const snd_pcm_channel_area_t **areas,
snd_pcm_uframes_t *offset,
snd_pcm_uframes_t *frames)
+{
+ int err;
+
+ snd_pcm_lock(pcm);
+ err = __snd_pcm_mmap_begin(pcm, areas, offset, frames);
+ snd_pcm_unlock(pcm);
+ return err;
+}
+
+#ifndef DOC_HIDDEN
+/* locked version */
+int __snd_pcm_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas,
+ snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames)
{
snd_pcm_uframes_t cont;
snd_pcm_uframes_t f;
*frames = f;
return 0;
}
+#endif
/**
* \brief Application has completed the access to area requested with #snd_pcm_mmap_begin
*
* Look to the \ref example_test_pcm "Sine-wave generator" example
* for more details about the generate_sine function.
+ *
+ * The function is thread-safe when built with the proper option.
*/
snd_pcm_sframes_t snd_pcm_mmap_commit(snd_pcm_t *pcm,
snd_pcm_uframes_t offset,
snd_pcm_uframes_t frames)
+{
+ snd_pcm_sframes_t result;
+
+ snd_pcm_lock(pcm);
+ result = __snd_pcm_mmap_commit(pcm, offset, frames);
+ snd_pcm_unlock(pcm);
+ return result;
+}
+
+#ifndef DOC_HIDDEN
+/* locked version*/
+snd_pcm_sframes_t __snd_pcm_mmap_commit(snd_pcm_t *pcm,
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t frames)
{
assert(pcm);
if (CHECK_SANITY(offset != *pcm->appl.ptr % pcm->buffer_size)) {
return pcm->fast_ops->mmap_commit(pcm->fast_op_arg, offset, frames);
}
-#ifndef DOC_HIDDEN
-
int _snd_pcm_poll_descriptor(snd_pcm_t *pcm)
{
assert(pcm);
void *buf)
{
unsigned int channel;
- unsigned int channels = pcm->channels;
+ unsigned int channels;
+
+ snd_pcm_lock(pcm);
+ channels = pcm->channels;
for (channel = 0; channel < channels; ++channel, ++areas) {
areas->addr = buf;
areas->first = channel * pcm->sample_bits;
areas->step = pcm->frame_bits;
}
+ snd_pcm_unlock(pcm);
}
void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas,
void **bufs)
{
unsigned int channel;
- unsigned int channels = pcm->channels;
+ unsigned int channels;
+
+ snd_pcm_lock(pcm);
+ channels = pcm->channels;
for (channel = 0; channel < channels; ++channel, ++areas, ++bufs) {
areas->addr = *bufs;
areas->first = 0;
areas->step = pcm->sample_bits;
}
+ snd_pcm_unlock(pcm);
}
snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas,
if (size == 0)
return 0;
+ __snd_pcm_lock(pcm); /* forced lock */
while (size > 0) {
snd_pcm_uframes_t frames;
snd_pcm_sframes_t avail;
_again:
- state = snd_pcm_state(pcm);
+ state = __snd_pcm_state(pcm);
switch (state) {
case SND_PCM_STATE_PREPARED:
- err = snd_pcm_start(pcm);
+ err = __snd_pcm_start(pcm);
if (err < 0)
goto _end;
break;
case SND_PCM_STATE_RUNNING:
- err = snd_pcm_hwsync(pcm);
+ err = __snd_pcm_hwsync(pcm);
if (err < 0)
goto _end;
break;
err = -EBADFD;
goto _end;
}
- avail = snd_pcm_avail_update(pcm);
+ avail = __snd_pcm_avail_update(pcm);
if (avail < 0) {
err = avail;
goto _end;
goto _end;
}
- err = snd_pcm_wait(pcm, -1);
+ err = __snd_pcm_wait_in_lock(pcm, -1);
if (err < 0)
break;
goto _again;
xfer += frames;
}
_end:
+ __snd_pcm_unlock(pcm);
return xfer > 0 ? (snd_pcm_sframes_t) xfer : snd_pcm_check_error(pcm, err);
}
if (size == 0)
return 0;
+ __snd_pcm_lock(pcm); /* forced lock */
while (size > 0) {
snd_pcm_uframes_t frames;
snd_pcm_sframes_t avail;
_again:
- state = snd_pcm_state(pcm);
+ state = __snd_pcm_state(pcm);
switch (state) {
case SND_PCM_STATE_PREPARED:
case SND_PCM_STATE_PAUSED:
break;
case SND_PCM_STATE_RUNNING:
- err = snd_pcm_hwsync(pcm);
+ err = __snd_pcm_hwsync(pcm);
if (err < 0)
goto _end;
break;
err = -EBADFD;
goto _end;
}
- avail = snd_pcm_avail_update(pcm);
+ avail = __snd_pcm_avail_update(pcm);
if (avail < 0) {
err = avail;
goto _end;
snd_pcm_sframes_t hw_avail = pcm->buffer_size - avail;
hw_avail += frames;
/* some plugins might automatically start the stream */
- state = snd_pcm_state(pcm);
+ state = __snd_pcm_state(pcm);
if (state == SND_PCM_STATE_PREPARED &&
hw_avail >= (snd_pcm_sframes_t) pcm->start_threshold) {
- err = snd_pcm_start(pcm);
+ err = __snd_pcm_start(pcm);
if (err < 0)
goto _end;
}
xfer += frames;
}
_end:
+ __snd_pcm_unlock(pcm);
return xfer > 0 ? (snd_pcm_sframes_t) xfer : snd_pcm_check_error(pcm, err);
}
events = pfds[0].revents;
if (events & POLLIN) {
snd_pcm_uframes_t avail;
- snd_pcm_avail_update(pcm);
+ __snd_pcm_avail_update(pcm);
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
events |= POLLOUT;
events &= ~POLLIN;
snd_pcm_direct_clear_timer_queue(dmix);
events &= ~(POLLOUT|POLLIN);
/* additional check */
- switch (snd_pcm_state(pcm)) {
+ switch (__snd_pcm_state(pcm)) {
case SND_PCM_STATE_XRUN:
case SND_PCM_STATE_SUSPENDED:
case SND_PCM_STATE_SETUP:
return 0;
}
-static int snd_pcm_dmix_drain(snd_pcm_t *pcm)
+/* locked version */
+static int __snd_pcm_dmix_drain(snd_pcm_t *pcm)
{
snd_pcm_direct_t *dmix = pcm->private_data;
snd_pcm_uframes_t stop_threshold;
return 0;
}
+static int snd_pcm_dmix_drain(snd_pcm_t *pcm)
+{
+ int err;
+
+ snd_pcm_lock(pcm);
+ err = __snd_pcm_dmix_drain(pcm);
+ snd_pcm_unlock(pcm);
+ return err;
+}
+
static int snd_pcm_dmix_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
{
return -EIO;
return 0;
}
-static int snd_pcm_dshare_drain(snd_pcm_t *pcm)
+/* locked version */
+static int __snd_pcm_dshare_drain(snd_pcm_t *pcm)
{
snd_pcm_direct_t *dshare = pcm->private_data;
snd_pcm_uframes_t stop_threshold;
return 0;
}
+static int snd_pcm_dshare_drain(snd_pcm_t *pcm)
+{
+ int err;
+
+ snd_pcm_lock(pcm);
+ err = __snd_pcm_dshare_drain(pcm);
+ snd_pcm_unlock(pcm);
+ return err;
+}
+
static int snd_pcm_dshare_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
{
return -EIO;
return 0;
}
-static int snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
+/* locked version */
+static int __snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
{
snd_pcm_direct_t *dsnoop = pcm->private_data;
snd_pcm_uframes_t stop_threshold;
break;
if (pcm->mode & SND_PCM_NONBLOCK)
return -EAGAIN;
- snd_pcm_wait(pcm, -1);
+ __snd_pcm_wait_in_lock(pcm, -1);
}
pcm->stop_threshold = stop_threshold;
return snd_pcm_dsnoop_drop(pcm);
}
+static int snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
+{
+ int err;
+
+ snd_pcm_lock(pcm);
+ err = __snd_pcm_dsnoop_drain(pcm);
+ snd_pcm_unlock(pcm);
+ return err;
+}
+
static int snd_pcm_dsnoop_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
{
return -EIO;
return err;
}
+/* locking */
static int snd_pcm_file_drain(snd_pcm_t *pcm)
{
snd_pcm_file_t *file = pcm->private_data;
int err = snd_pcm_drain(file->gen.slave);
if (err >= 0) {
+ __snd_pcm_lock(pcm);
snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
assert(file->wbuf_used_bytes == 0);
+ __snd_pcm_unlock(pcm);
}
return err;
}
return err;
}
+/* locking */
static snd_pcm_sframes_t snd_pcm_file_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
snd_pcm_file_t *file = pcm->private_data;
snd_pcm_channel_area_t areas[pcm->channels];
- snd_pcm_sframes_t n = snd_pcm_writei(file->gen.slave, buffer, size);
+ snd_pcm_sframes_t n = _snd_pcm_writei(file->gen.slave, buffer, size);
if (n > 0) {
snd_pcm_areas_from_buf(pcm, areas, (void*) buffer);
+ __snd_pcm_lock(pcm);
snd_pcm_file_add_frames(pcm, areas, 0, n);
+ __snd_pcm_unlock(pcm);
}
return n;
}
+/* locking */
static snd_pcm_sframes_t snd_pcm_file_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
{
snd_pcm_file_t *file = pcm->private_data;
snd_pcm_channel_area_t areas[pcm->channels];
- snd_pcm_sframes_t n = snd_pcm_writen(file->gen.slave, bufs, size);
+ snd_pcm_sframes_t n = _snd_pcm_writen(file->gen.slave, bufs, size);
if (n > 0) {
snd_pcm_areas_from_bufs(pcm, areas, bufs);
+ __snd_pcm_lock(pcm);
snd_pcm_file_add_frames(pcm, areas, 0, n);
+ __snd_pcm_unlock(pcm);
}
return n;
}
+/* locking */
static snd_pcm_sframes_t snd_pcm_file_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
{
snd_pcm_file_t *file = pcm->private_data;
snd_pcm_sframes_t n;
- n = snd_pcm_readi(file->gen.slave, buffer, size);
+ n = _snd_pcm_readi(file->gen.slave, buffer, size);
if (n <= 0)
return n;
if (file->ifd >= 0) {
+ __snd_pcm_lock(pcm);
n = read(file->ifd, buffer, n * pcm->frame_bits / 8);
+ __snd_pcm_unlock(pcm);
if (n < 0)
return n;
return n * 8 / pcm->frame_bits;
return n;
}
+/* locking */
static snd_pcm_sframes_t snd_pcm_file_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
{
snd_pcm_file_t *file = pcm->private_data;
return 0; /* TODO: Noninterleaved read */
}
- n = snd_pcm_readn(file->gen.slave, bufs, size);
+ n = _snd_pcm_readn(file->gen.slave, bufs, size);
return n;
}
snd_pcm_sframes_t snd_pcm_generic_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
snd_pcm_generic_t *generic = pcm->private_data;
- return snd_pcm_writei(generic->slave, buffer, size);
+ return _snd_pcm_writei(generic->slave, buffer, size);
}
snd_pcm_sframes_t snd_pcm_generic_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
{
snd_pcm_generic_t *generic = pcm->private_data;
- return snd_pcm_writen(generic->slave, bufs, size);
+ return _snd_pcm_writen(generic->slave, bufs, size);
}
snd_pcm_sframes_t snd_pcm_generic_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
{
snd_pcm_generic_t *generic = pcm->private_data;
- return snd_pcm_readi(generic->slave, buffer, size);
+ return _snd_pcm_readi(generic->slave, buffer, size);
}
snd_pcm_sframes_t snd_pcm_generic_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
{
snd_pcm_generic_t *generic = pcm->private_data;
- return snd_pcm_readn(generic->slave, bufs, size);
+ return _snd_pcm_readn(generic->slave, bufs, size);
}
snd_pcm_sframes_t snd_pcm_generic_mmap_commit(snd_pcm_t *pcm,
int ok = 0;
while (1) {
- avail1 = snd_pcm_avail_update(pcm);
+ avail1 = __snd_pcm_avail_update(pcm);
if (avail1 < 0)
return avail1;
if (ok && (snd_pcm_uframes_t)avail1 == *avail)
pcm->poll_fd = fd;
pcm->poll_events = info.stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
pcm->tstamp_type = tstamp_type;
+#ifdef THREAD_SAFE_API
+ pcm->thread_safe = 1;
+#endif
ret = snd_pcm_hw_mmap_status(pcm);
if (ret < 0) {
} ioplug_priv_t;
/* update the hw pointer */
+/* called in lock */
static void snd_pcm_ioplug_hw_ptr_update(snd_pcm_t *pcm)
{
ioplug_priv_t *io = pcm->private_data;
static int snd_pcm_ioplug_prepare(snd_pcm_t *pcm)
{
ioplug_priv_t *io = pcm->private_data;
+ int err = 0;
io->data->state = SND_PCM_STATE_PREPARED;
snd_pcm_ioplug_reset(pcm);
- if (io->data->callback->prepare)
- return io->data->callback->prepare(io->data);
- return 0;
+ if (io->data->callback->prepare) {
+ snd_pcm_unlock(pcm); /* to avoid deadlock */
+ err = io->data->callback->prepare(io->data);
+ snd_pcm_lock(pcm);
+ }
+ return err;
}
static const int hw_params_type[SND_PCM_IOPLUG_HW_PARAMS] = {
static int snd_pcm_ioplug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
{
ioplug_priv_t *io = pcm->private_data;
+ int err = 0;
- if (io->data->callback->sw_params)
- return io->data->callback->sw_params(io->data, params);
+ if (io->data->callback->sw_params) {
+ snd_pcm_unlock(pcm); /* to avoid deadlock */
+ err = io->data->callback->sw_params(io->data, params);
+ snd_pcm_lock(pcm);
+ }
return 0;
}
return 0;
}
+/* need own locking */
static int snd_pcm_ioplug_drain(snd_pcm_t *pcm)
{
ioplug_priv_t *io = pcm->private_data;
+ int err;
if (io->data->state == SND_PCM_STATE_OPEN)
return -EBADFD;
if (io->data->callback->drain)
io->data->callback->drain(io->data);
- return snd_pcm_ioplug_drop(pcm);
+ snd_pcm_lock(pcm);
+ err = snd_pcm_ioplug_drop(pcm);
+ snd_pcm_unlock(pcm);
+ return err;
}
static int snd_pcm_ioplug_pause(snd_pcm_t *pcm, int enable)
return frames;
}
+/* need own locking */
static int snd_pcm_ioplug_resume(snd_pcm_t *pcm)
{
ioplug_priv_t *io = pcm->private_data;
return 0;
}
+/* called in lock */
static snd_pcm_sframes_t ioplug_priv_transfer_areas(snd_pcm_t *pcm,
const snd_pcm_channel_area_t *areas,
snd_pcm_uframes_t offset,
const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t ofs, frames = size;
- snd_pcm_mmap_begin(pcm, &areas, &ofs, &frames);
+ __snd_pcm_mmap_begin(pcm, &areas, &ofs, &frames);
if (ofs != offset)
return -EIO;
return ioplug_priv_transfer_areas(pcm, areas, offset, frames);
snd_pcm_uframes_t offset, size = UINT_MAX;
snd_pcm_sframes_t result;
- snd_pcm_mmap_begin(pcm, &areas, &offset, &size);
+ __snd_pcm_mmap_begin(pcm, &areas, &offset, &size);
result = io->data->callback->transfer(io->data, areas, offset, size);
if (result < 0)
return result;
static int snd_pcm_ioplug_poll_descriptors_count(snd_pcm_t *pcm)
{
ioplug_priv_t *io = pcm->private_data;
+ int err = 1;
- if (io->data->callback->poll_descriptors_count)
- return io->data->callback->poll_descriptors_count(io->data);
- else
- return 1;
+ if (io->data->callback->poll_descriptors_count) {
+ snd_pcm_unlock(pcm); /* to avoid deadlock */
+ err = io->data->callback->poll_descriptors_count(io->data);
+ snd_pcm_lock(pcm);
+ }
+ return err;
}
static int snd_pcm_ioplug_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
{
ioplug_priv_t *io = pcm->private_data;
+ int err;
- if (io->data->callback->poll_descriptors)
- return io->data->callback->poll_descriptors(io->data, pfds, space);
+ if (io->data->callback->poll_descriptors) {
+ snd_pcm_unlock(pcm); /* to avoid deadlock */
+ err = io->data->callback->poll_descriptors(io->data, pfds, space);
+ snd_pcm_lock(pcm);
+ return err;
+ }
if (pcm->poll_fd < 0)
return -EIO;
if (space >= 1 && pfds) {
static int snd_pcm_ioplug_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
{
ioplug_priv_t *io = pcm->private_data;
+ int err;
- if (io->data->callback->poll_revents)
- return io->data->callback->poll_revents(io->data, pfds, nfds, revents);
- else
+ if (io->data->callback->poll_revents) {
+ snd_pcm_unlock(pcm); /* to avoid deadlock */
+ err = io->data->callback->poll_revents(io->data, pfds, nfds, revents);
+ snd_pcm_lock(pcm);
+ } else {
*revents = pfds->revents;
- return 0;
+ err = 0;
+ }
+ return err;
}
static int snd_pcm_ioplug_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
Finally, the dump callback is used to print the status of the plugin.
+Note that some callbacks (start, stop, pointer, transfer and pause)
+may be called inside the internal pthread mutex, and they shouldn't
+call the PCM functions again unnecessarily from the callback itself;
+otherwise it may lead to a deadlock.
+
The hw_params constraints can be defined via either
#snd_pcm_ioplug_set_param_minmax() and #snd_pcm_ioplug_set_param_list()
functions after calling #snd_pcm_ioplug_create().
#include "local.h"
+#ifdef THREAD_SAFE_API
+#include <pthread.h>
+#endif
+
#define SND_INTERVAL_INLINE
#include "interval.h"
typedef struct {
int (*close)(snd_pcm_t *pcm);
- int (*nonblock)(snd_pcm_t *pcm, int nonblock);
+ int (*nonblock)(snd_pcm_t *pcm, int nonblock); /* always locked */
int (*async)(snd_pcm_t *pcm, int sig, pid_t pid);
int (*info)(snd_pcm_t *pcm, snd_pcm_info_t *info);
int (*hw_refine)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
int (*hw_params)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
int (*hw_free)(snd_pcm_t *pcm);
- int (*sw_params)(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);
+ int (*sw_params)(snd_pcm_t *pcm, snd_pcm_sw_params_t *params); /* always locked */
int (*channel_info)(snd_pcm_t *pcm, snd_pcm_channel_info_t *info);
void (*dump)(snd_pcm_t *pcm, snd_output_t *out);
int (*mmap)(snd_pcm_t *pcm);
} snd_pcm_ops_t;
typedef struct {
- int (*status)(snd_pcm_t *pcm, snd_pcm_status_t *status);
- int (*prepare)(snd_pcm_t *pcm);
- int (*reset)(snd_pcm_t *pcm);
- int (*start)(snd_pcm_t *pcm);
- int (*drop)(snd_pcm_t *pcm);
- int (*drain)(snd_pcm_t *pcm);
- int (*pause)(snd_pcm_t *pcm, int enable);
- snd_pcm_state_t (*state)(snd_pcm_t *pcm);
- int (*hwsync)(snd_pcm_t *pcm);
- int (*delay)(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp);
- int (*resume)(snd_pcm_t *pcm);
+ int (*status)(snd_pcm_t *pcm, snd_pcm_status_t *status); /* locked */
+ int (*prepare)(snd_pcm_t *pcm); /* locked */
+ int (*reset)(snd_pcm_t *pcm); /* locked */
+ int (*start)(snd_pcm_t *pcm); /* locked */
+ int (*drop)(snd_pcm_t *pcm); /* locked */
+ int (*drain)(snd_pcm_t *pcm); /* need own locking */
+ int (*pause)(snd_pcm_t *pcm, int enable); /* locked */
+ snd_pcm_state_t (*state)(snd_pcm_t *pcm); /* locked */
+ int (*hwsync)(snd_pcm_t *pcm); /* locked */
+ int (*delay)(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp); /* locked */
+ int (*resume)(snd_pcm_t *pcm); /* need own locking */
int (*link)(snd_pcm_t *pcm1, snd_pcm_t *pcm2);
int (*link_slaves)(snd_pcm_t *pcm, snd_pcm_t *master);
int (*unlink)(snd_pcm_t *pcm);
- snd_pcm_sframes_t (*rewindable)(snd_pcm_t *pcm);
- snd_pcm_sframes_t (*rewind)(snd_pcm_t *pcm, snd_pcm_uframes_t frames);
- snd_pcm_sframes_t (*forwardable)(snd_pcm_t *pcm);
- snd_pcm_sframes_t (*forward)(snd_pcm_t *pcm, snd_pcm_uframes_t frames);
- snd_pcm_sframes_t (*writei)(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
- snd_pcm_sframes_t (*writen)(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size);
- snd_pcm_sframes_t (*readi)(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size);
- snd_pcm_sframes_t (*readn)(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size);
- snd_pcm_sframes_t (*avail_update)(snd_pcm_t *pcm);
- snd_pcm_sframes_t (*mmap_commit)(snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t size);
- int (*htimestamp)(snd_pcm_t *pcm, snd_pcm_uframes_t *avail, snd_htimestamp_t *tstamp);
- int (*poll_descriptors_count)(snd_pcm_t *pcm);
- int (*poll_descriptors)(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
- int (*poll_revents)(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
+ snd_pcm_sframes_t (*rewindable)(snd_pcm_t *pcm); /* locked */
+ snd_pcm_sframes_t (*rewind)(snd_pcm_t *pcm, snd_pcm_uframes_t frames); /* locked */
+ snd_pcm_sframes_t (*forwardable)(snd_pcm_t *pcm); /* locked */
+ snd_pcm_sframes_t (*forward)(snd_pcm_t *pcm, snd_pcm_uframes_t frames); /* locked */
+ snd_pcm_sframes_t (*writei)(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size); /* need own locking */
+ snd_pcm_sframes_t (*writen)(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size); /* need own locking */
+ snd_pcm_sframes_t (*readi)(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size); /* need own locking */
+ snd_pcm_sframes_t (*readn)(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size); /* need own locking */
+ snd_pcm_sframes_t (*avail_update)(snd_pcm_t *pcm); /* locked */
+ snd_pcm_sframes_t (*mmap_commit)(snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t size); /* locked */
+ int (*htimestamp)(snd_pcm_t *pcm, snd_pcm_uframes_t *avail, snd_htimestamp_t *tstamp); /* locked */
+ int (*poll_descriptors_count)(snd_pcm_t *pcm); /* locked */
+ int (*poll_descriptors)(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space); /* locked */
+ int (*poll_revents)(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); /* locked */
int (*may_wait_for_avail_min)(snd_pcm_t *pcm, snd_pcm_uframes_t avail);
} snd_pcm_fast_ops_t;
snd_pcm_t *fast_op_arg;
void *private_data;
struct list_head async_handlers;
+#ifdef THREAD_SAFE_API
+ int thread_safe;
+ pthread_mutex_t lock;
+#endif
};
/* make local functions really local */
#define _snd_pcm_link_descriptor _snd_pcm_poll_descriptor /* FIXME */
#define _snd_pcm_async_descriptor _snd_pcm_poll_descriptor /* FIXME */
+/* locked versions */
+int __snd_pcm_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas,
+ snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames);
+snd_pcm_sframes_t __snd_pcm_mmap_commit(snd_pcm_t *pcm,
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t frames);
+int __snd_pcm_wait_in_lock(snd_pcm_t *pcm, int timeout);
+
+static inline snd_pcm_sframes_t __snd_pcm_avail_update(snd_pcm_t *pcm)
+{
+ return pcm->fast_ops->avail_update(pcm->fast_op_arg);
+}
+
+static inline int __snd_pcm_start(snd_pcm_t *pcm)
+{
+ return pcm->fast_ops->start(pcm->fast_op_arg);
+}
+
+static inline snd_pcm_state_t __snd_pcm_state(snd_pcm_t *pcm)
+{
+ return pcm->fast_ops->state(pcm->fast_op_arg);
+}
+
+static inline int __snd_pcm_hwsync(snd_pcm_t *pcm)
+{
+ return pcm->fast_ops->hwsync(pcm->fast_op_arg);
+}
+
+static inline int __snd_pcm_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
+{
+ return pcm->fast_ops->delay(pcm->fast_op_arg, delayp);
+}
+
/* handle special error cases */
static inline int snd_pcm_check_error(snd_pcm_t *pcm, int err)
{
if (err == -EINTR) {
- switch (snd_pcm_state(pcm)) {
+ switch (__snd_pcm_state(pcm)) {
case SND_PCM_STATE_XRUN:
return -EPIPE;
case SND_PCM_STATE_SUSPENDED:
static inline const snd_pcm_channel_area_t *snd_pcm_mmap_areas(snd_pcm_t *pcm)
{
if (pcm->stopped_areas &&
- snd_pcm_state(pcm) != SND_PCM_STATE_RUNNING)
+ __snd_pcm_state(pcm) != SND_PCM_STATE_RUNNING)
return pcm->stopped_areas;
return pcm->running_areas;
}
static inline snd_pcm_sframes_t _snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
+ /* lock handled in the callback */
return pcm->fast_ops->writei(pcm->fast_op_arg, buffer, size);
}
static inline snd_pcm_sframes_t _snd_pcm_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
{
+ /* lock handled in the callback */
return pcm->fast_ops->writen(pcm->fast_op_arg, bufs, size);
}
static inline snd_pcm_sframes_t _snd_pcm_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
{
+ /* lock handled in the callback */
return pcm->fast_ops->readi(pcm->fast_op_arg, buffer, size);
}
static inline snd_pcm_sframes_t _snd_pcm_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
{
+ /* lock handled in the callback */
return pcm->fast_ops->readn(pcm->fast_op_arg, bufs, size);
}
}
#define PCMINABORT(pcm) (((pcm)->mode & SND_PCM_ABORT) != 0)
+
+#ifdef THREAD_SAFE_API
+static inline void __snd_pcm_lock(snd_pcm_t *pcm)
+{
+ pthread_mutex_lock(&pcm->lock);
+}
+static inline void __snd_pcm_unlock(snd_pcm_t *pcm)
+{
+ pthread_mutex_unlock(&pcm->lock);
+}
+static inline void snd_pcm_lock(snd_pcm_t *pcm)
+{
+ if (!pcm->thread_safe)
+ pthread_mutex_lock(&pcm->lock);
+}
+static inline void snd_pcm_unlock(snd_pcm_t *pcm)
+{
+ if (!pcm->thread_safe)
+ pthread_mutex_unlock(&pcm->lock);
+}
+#else /* THREAD_SAFE_API */
+#define __snd_pcm_lock(pcm) do {} while (0)
+#define __snd_pcm_unlock(pcm) do {} while (0)
+#define snd_pcm_lock(pcm) do {} while (0)
+#define snd_pcm_unlock(pcm) do {} while (0)
+#endif /* THREAD_SAFE_API */
snd_pcm_uframes_t frames = size;
snd_pcm_sframes_t result;
- snd_pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
+ __snd_pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
snd_pcm_areas_copy(pcm_areas, pcm_offset,
areas, offset,
pcm->channels,
frames, pcm->format);
- result = snd_pcm_mmap_commit(pcm, pcm_offset, frames);
+ result = __snd_pcm_mmap_commit(pcm, pcm_offset, frames);
if (result < 0)
return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
offset += result;
snd_pcm_uframes_t frames = size;
snd_pcm_sframes_t result;
- snd_pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
+ __snd_pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
snd_pcm_areas_copy(areas, offset,
pcm_areas, pcm_offset,
pcm->channels,
frames, pcm->format);
- result = snd_pcm_mmap_commit(pcm, pcm_offset, frames);
+ result = __snd_pcm_mmap_commit(pcm, pcm_offset, frames);
if (result < 0)
return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
offset += result;
return 0;
}
+/* called in pcm lock */
snd_pcm_sframes_t snd_pcm_write_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset,
snd_pcm_uframes_t size)
{
{
const snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm);
const char *buf = snd_pcm_channel_area_addr(a, offset);
+ snd_pcm_unlock(pcm); /* to avoid deadlock */
err = _snd_pcm_writei(pcm, buf, frames);
+ snd_pcm_lock(pcm);
if (err >= 0)
frames = err;
break;
const snd_pcm_channel_area_t *a = &areas[c];
bufs[c] = snd_pcm_channel_area_addr(a, offset);
}
+ snd_pcm_unlock(pcm); /* to avoid deadlock */
err = _snd_pcm_writen(pcm, bufs, frames);
+ snd_pcm_lock(pcm);
if (err >= 0)
frames = err;
break;
return err;
}
+/* called in pcm lock */
snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset,
snd_pcm_uframes_t size)
{
{
const snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm);
char *buf = snd_pcm_channel_area_addr(a, offset);
+ snd_pcm_unlock(pcm); /* to avoid deadlock */
err = _snd_pcm_readi(pcm, buf, frames);
+ snd_pcm_lock(pcm);
if (err >= 0)
frames = err;
break;
const snd_pcm_channel_area_t *a = &areas[c];
bufs[c] = snd_pcm_channel_area_addr(a, offset);
}
+ snd_pcm_unlock(pcm); /* to avoid deadlock */
err = _snd_pcm_readn(pcm->fast_op_arg, bufs, frames);
+ snd_pcm_lock(pcm);
if (err >= 0)
frames = err;
break;
return snd_pcm_poll_descriptors_revents(rate->gen.slave, pfds, nfds, revents);
}
+/* locking */
static int snd_pcm_rate_drain(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_uframes_t size, ofs, saved_avail_min;
snd_pcm_sw_params_t sw_params;
+ __snd_pcm_lock(pcm);
/* temporarily set avail_min to one */
sw_params = rate->sw_params;
saved_avail_min = sw_params.avail_min;
ofs = rate->last_commit_ptr % pcm->buffer_size;
while (size > 0) {
snd_pcm_uframes_t psize, spsize;
+ int err;
- if (snd_pcm_wait(rate->gen.slave, -1) < 0)
+ err = __snd_pcm_wait_in_lock(rate->gen.slave, -1);
+ if (err < 0)
break;
if (size > pcm->period_size) {
psize = pcm->period_size;
}
sw_params.avail_min = saved_avail_min;
snd_pcm_sw_params(rate->gen.slave, &sw_params);
+ __snd_pcm_unlock(pcm);
}
return snd_pcm_drain(rate->gen.slave);
}
snd_pcm_route_t *route = pcm->private_data;
if (!route->chmap)
return 0;
- if (snd_pcm_state(pcm) != SND_PCM_STATE_PREPARED)
+ if (__snd_pcm_state(pcm) != SND_PCM_STATE_PREPARED)
return 0;
/* Check if we really need to set the chmap or not.