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
thread scheduling.
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
FIFO.
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
the FIFO.
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
usual place:
http://www.angelfire.com/ar/agc/download/
(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ā¦
//David
.- 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: