mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-11-02 09:01:48 -05:00
The following patch comes from the realization that at least ARM code
for atomics is quite broken and nobody has cared for a decade.
A quick dive shows that only snd_atomic_{read,write}_{begin,end}
appear to be used widely. These are implemented using wmb/rmb.
Only other use of atomic functions is in pcm_meter.c.
The #SND_PCM_TYPE_METER plugin type appears rarely, if ever, used.
I presume these days anyone who wants a meter/scope will do in pulseaudio
layer instead of alsa.
It would seem better fit to have pcm_meter in alsa-plugins instead
of alsa-lib, but I guess that would be an ABI break...
So instead, I'm proposing here
1. Removal of all hand-crafted atomics from iatomic.h apart from barriers,
which are used in snd_atomic_{read,write}_{begin,end}.
2. Using __sync_synchronize as the default fallback for barriers. This
has been available since gcc 4.1, so it shouldn't be a problem.
3. Defining the few atomics used by pcm_meter.c withing pcm_meter.c
itself, using gcc atomic builtins[1].
4. Since gcc atomic builtins are available only since gcc 4.7, add a check for
that in gcc configure.in, and don't build pcm meter plugin if using
older gcc.
The last point has the impact, that if there actually is someone who 1)
uses the meter plugin 2) wants to upgrade to 2014 alsa-lib 3) but
does not want to use a 2012+ gcc - that someone will be inconvenienced.
Finally remove the unneeded configure check for cpu type. We can
trust the gcc to set right flags for us.
[1] http://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
Signed-off-by: Riku Voipio <riku.voipio@linaro.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
170 lines
4.5 KiB
C
170 lines
4.5 KiB
C
#ifndef __ALSA_IATOMIC_H
|
|
#define __ALSA_IATOMIC_H
|
|
|
|
#ifdef __i386__
|
|
#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory")
|
|
#define rmb() mb()
|
|
#define wmb() __asm__ __volatile__ ("": : :"memory")
|
|
#define IATOMIC_DEFINED 1
|
|
#endif
|
|
|
|
#ifdef __x86_64__
|
|
#define mb() asm volatile("mfence":::"memory")
|
|
#define rmb() asm volatile("lfence":::"memory")
|
|
#define wmb() asm volatile("sfence":::"memory")
|
|
#define IATOMIC_DEFINED 1
|
|
#endif
|
|
|
|
#ifdef __ia64__
|
|
/*
|
|
* Macros to force memory ordering. In these descriptions, "previous"
|
|
* and "subsequent" refer to program order; "visible" means that all
|
|
* architecturally visible effects of a memory access have occurred
|
|
* (at a minimum, this means the memory has been read or written).
|
|
*
|
|
* wmb(): Guarantees that all preceding stores to memory-
|
|
* like regions are visible before any subsequent
|
|
* stores and that all following stores will be
|
|
* visible only after all previous stores.
|
|
* rmb(): Like wmb(), but for reads.
|
|
* mb(): wmb()/rmb() combo, i.e., all previous memory
|
|
* accesses are visible before all subsequent
|
|
* accesses and vice versa. This is also known as
|
|
* a "fence."
|
|
*
|
|
* Note: "mb()" and its variants cannot be used as a fence to order
|
|
* accesses to memory mapped I/O registers. For that, mf.a needs to
|
|
* be used. However, we don't want to always use mf.a because (a)
|
|
* it's (presumably) much slower than mf and (b) mf.a is supported for
|
|
* sequential memory pages only.
|
|
*/
|
|
#define mb() __asm__ __volatile__ ("mf" ::: "memory")
|
|
#define rmb() mb()
|
|
#define wmb() mb()
|
|
|
|
#define IATOMIC_DEFINED 1
|
|
|
|
#endif /* __ia64__ */
|
|
|
|
#ifdef __alpha__
|
|
|
|
#define mb() \
|
|
__asm__ __volatile__("mb": : :"memory")
|
|
|
|
#define rmb() \
|
|
__asm__ __volatile__("mb": : :"memory")
|
|
|
|
#define wmb() \
|
|
__asm__ __volatile__("wmb": : :"memory")
|
|
|
|
#define IATOMIC_DEFINED 1
|
|
|
|
#endif /* __alpha__ */
|
|
|
|
#ifdef __powerpc__
|
|
|
|
/*
|
|
* Memory barrier.
|
|
* The sync instruction guarantees that all memory accesses initiated
|
|
* by this processor have been performed (with respect to all other
|
|
* mechanisms that access memory). The eieio instruction is a barrier
|
|
* providing an ordering (separately) for (a) cacheable stores and (b)
|
|
* loads and stores to non-cacheable memory (e.g. I/O devices).
|
|
*
|
|
* mb() prevents loads and stores being reordered across this point.
|
|
* rmb() prevents loads being reordered across this point.
|
|
* wmb() prevents stores being reordered across this point.
|
|
*
|
|
* We can use the eieio instruction for wmb, but since it doesn't
|
|
* give any ordering guarantees about loads, we have to use the
|
|
* stronger but slower sync instruction for mb and rmb.
|
|
*/
|
|
#define mb() __asm__ __volatile__ ("sync" : : : "memory")
|
|
#define rmb() __asm__ __volatile__ ("sync" : : : "memory")
|
|
#define wmb() __asm__ __volatile__ ("eieio" : : : "memory")
|
|
|
|
#define IATOMIC_DEFINED 1
|
|
|
|
#endif /* __powerpc__ */
|
|
|
|
#ifndef IATOMIC_DEFINED
|
|
|
|
/* Generic __sync_synchronize is available from gcc 4.1 */
|
|
|
|
#define mb() __sync_synchronize()
|
|
#define rmb() mb()
|
|
#define wmb() mb()
|
|
|
|
#define IATOMIC_DEFINED 1
|
|
|
|
#endif /* IATOMIC_DEFINED */
|
|
|
|
/*
|
|
* Atomic read/write
|
|
* Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
|
|
*/
|
|
|
|
/* Max number of times we must spin on a spin-lock calling sched_yield().
|
|
After MAX_SPIN_COUNT iterations, we put the calling thread to sleep. */
|
|
|
|
#ifndef MAX_SPIN_COUNT
|
|
#define MAX_SPIN_COUNT 50
|
|
#endif
|
|
|
|
/* Duration of sleep (in nanoseconds) when we can't acquire a spin-lock
|
|
after MAX_SPIN_COUNT iterations of sched_yield().
|
|
This MUST BE > 2ms.
|
|
(Otherwise the kernel does busy-waiting for real-time threads,
|
|
giving other threads no chance to run.) */
|
|
|
|
#ifndef SPIN_SLEEP_DURATION
|
|
#define SPIN_SLEEP_DURATION 2000001
|
|
#endif
|
|
|
|
typedef struct {
|
|
unsigned int begin, end;
|
|
} snd_atomic_write_t;
|
|
|
|
typedef struct {
|
|
volatile const snd_atomic_write_t *write;
|
|
unsigned int end;
|
|
} snd_atomic_read_t;
|
|
|
|
void snd_atomic_read_wait(snd_atomic_read_t *t);
|
|
|
|
static __inline__ void snd_atomic_write_init(snd_atomic_write_t *w)
|
|
{
|
|
w->begin = 0;
|
|
w->end = 0;
|
|
}
|
|
|
|
static __inline__ void snd_atomic_write_begin(snd_atomic_write_t *w)
|
|
{
|
|
w->begin++;
|
|
wmb();
|
|
}
|
|
|
|
static __inline__ void snd_atomic_write_end(snd_atomic_write_t *w)
|
|
{
|
|
wmb();
|
|
w->end++;
|
|
}
|
|
|
|
static __inline__ void snd_atomic_read_init(snd_atomic_read_t *r, snd_atomic_write_t *w)
|
|
{
|
|
r->write = w;
|
|
}
|
|
|
|
static __inline__ void snd_atomic_read_begin(snd_atomic_read_t *r)
|
|
{
|
|
r->end = r->write->end;
|
|
rmb();
|
|
}
|
|
|
|
static __inline__ int snd_atomic_read_ok(snd_atomic_read_t *r)
|
|
{
|
|
rmb();
|
|
return r->end == r->write->begin;
|
|
}
|
|
|
|
#endif /* __ALSA_IATOMIC_H */
|