And result in clicks/blank spaces. Unacceptable. I’m talking about
blocking the audio interrupt for microseconds at a time, while, for
example, a link list operation is completed.
Unfortunately I don’t think there is any way to implement it.
Why not? There are lock-free solutions for most kind of sync constructs,
although most of them are rather messy, uggly and/or awkward to code.
Single reader-single writer constructs are rather simple, though, as long
as you have atomic reads and/or atomic writes for some usable word size,
which is the case with most architectures, even on SMP machines.
You only have to worry about the speculative reads and writes a CPU may
do. A CPU is allowed to re-order execution, and to speculatively read a
value (prefetch) before it’s used. I think this has been discussed a lot
on the Linux Kernel mailing list. I’m not really a CPU expert, but I
understand that reads and writes don’t always take place as expected
without the explicit placement of a ‘memory barrier’, which can be either
a read or a write barrier.
That’s quite enough to worry about, actually.
That said (FYI really), I don’t see it being an issue with your code
really (haven’t looked really hard).
The only problem would be if the read/write offset write is somehow done
before the last memcpy() is completely finished. If the code is very
carefully optimized by the compiler, this could probably happen on a next
generation CPU with an extremely deep pipeline, but I don’t think it’s even
theoretically possible with P-IV, G4 and older CPUs.
The real problem is that I’d really
like some of the other locking semantics that usually come along with
locks, such as wake-ups.
Right, but that’s not portable, and generally not possible to implement in a
way that guarantees that you don’t lose the CPU when trying to wake up
another thread. (That’s why one shouldn’t use standard sync constructs from
within SCHED_FIFO real time threads in audio on Linux/lowlatency BTW; it
effectively cancels the advantages of SCHED_FIFO on current kernels.)
Your FIFO’s are always non-blocking. It would
be nice to not have to wait in a busy loop for the interrupt to occur.
Well, the FIFOs were originally designed for communication between threads
that manage their own timing, expecting the FIFOs never to interfere with
After a full audio fragment has been processed (created) you want to wait
without spinning for the next audio interrupt, and pass the buffer to the
driver at that point. I suppose a 'while (!interrupted) usleep(1000);'
type of loop is OK. Since a fragment will generally be on the order of a
few millisenconds long.
Unless you’re using a dedicated thread only to process audio and then pass it
on to the callback, you normally wouldn’t want to sleep on the FIFO anyway.
If you are using a dedicated audio thread, why can’t the code be in the
callback, in the main loop of the application (provided it’s looping
continously at a sufficient rate), or in a thread that schedules
"periodically" using SDL_Delay() or something?
An example of where the non-blocking FIFO fits in perfectly would be a game
that runs the audio engine inside the audio callback, passing control
commands (“soundsource::start”, “soundsource::volume” etc) from either the
main loop, or (in the case of a decoupled game loop), from within the control
system thread. The audio callback would just read and process all commands on
the input FIFO every time it’s invoked, writing any responses to the return
No need for waking anything up, as both the game/control system thread and
the audio engine have different sources of timing that essentially define
their respective need for timing resolution. No need waking the control
system thread up to sync with audio events as the CS must stick to it’s fixed
"hartbeat", and no need to “wake up” the audio callback, as it can’t bypass
the audio buffering(*) anyway.
(*) Not quite true, if we’re using a shared memory audio API, rather than the
current SDL interface - but that’s another story. You don’t even need to
use a separate audio thread or callback at all with such an interface.
Try passing info (pointers to ready buffers) over a lock-free FIFO, for
example… If you want it rock solid and/or want to reduce the number of
buffers floating around, use another FIFO so the callback/ISR can return
buffers when they’re not needed any more.
It would be nice to block here waiting for the free buffer to come out of
If you’re using a dedicated audio thread; yes, but that’s not the kind of
setup this FIFO is designed for. (See above.)
The simplest way would be to have the main loop (doing video updates at
whatever speed the machine can cope with) do the same thing as the audio
callback; ie read and process all data in the “input” FIFO once per loop/call.
Of course, you’ll need enough buffering to deal with the timing of the main
loop, but if that results in too high latency, you’re probably not going to
want to use an audio engine that’s too indeterministic to run inside the
audio callback anyway. It’s not very likely that you’ll do much better with a
separate audio thread than inside the main loop, on most operating systems,
especially not if the main loop can use up lots of CPU time. (Note: It’s
entirely different with Linux/lowlatency and real RTOSes like QNX.)
I have a FIFO that I use for all sorts of things on various platforms,
and is should work on PPC as well, I think. I just uploaded a stripped
version (no Linux kernel driver versions of sfifo_read/write()) to the
(Do run the test program [make test] fist, just in case! It could
potentially break if the compiler does things differently, but the only
requirement is that buffer offsets are updated after data is read of
written, so I can’t see how…
I looked briefly at your code. I think there’s a slight bug. In
sfifo_init you set the f->size to the next greatest power of two to the
size passed in the following loop:
for(; f->size < size; f->size <<= 1);
And yet the actual malloc only allocates ‘size’ (not f->size) bytes. Then
you go on to use the entire f->size number of bytes throughout.
Oops. Never noticed, as I never request non-power-of-two buffer sizes.
Looks nice though, generally. Too bad there’s no portable way to do
non-racy sleep/wakeup stuff (which of course would need locking
Tricky stuff, indeed. Even when hacking the kernel, you eventually arrive at
the point where you have to make the scheduler aware of the fact that a new
thread has just been made runnable, and that’s where you hit a spinlock or
similar construct, protecting the runnable task list…
.- M A I A -------------------------------------------------.
| Multimedia Application Integration Architecture |
| A Free/Open Source Plugin API for Professional Multimedia |
----------------------> http://www.linuxaudiodev.com/maia -' .- David Olofson -------------------------------------------. | Audio Hacker - Open Source Advocate - Singer - Songwriter |--------------------------------------> david at linuxdj.com -'On Monday 23 April 2001 07:17, david at ultramaster.com wrote:
On Thursday 19 April 2001 23:36, Darrell Walisser wrote: