[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Discuss-gnuradio] Thread-Safe Blocking?
From: |
Michael Dickens |
Subject: |
Re: [Discuss-gnuradio] Thread-Safe Blocking? |
Date: |
Sun, 18 Dec 2005 14:46:17 -0500 |
Sorry for the delayed reply; just not enough time in the day.
1) On Dec 17, 2005, at 10:28 AM, Robert McGwier wrote:
I think the question was probably more than "what tools" but I
could be wrong.
No, I had gotten to the point where "what tools" was all I was
looking for. But your reply let me know that my most-self-determined
understanding was correct.
2) On Dec 17, 2005, at 12:28 PM, Robert McGwier wrote:
direction is release from gr-audio-osx using semaphores, flags,
etc. but never does it by releasing a mutex which has been acquired
by gr since they [do] (and will) block the audio callback. If the
data is not ready for the audio callback, issue an error and fill
in the buffers with zeros, the last data, whatever.
The only time gr-audio-osx truly blocks ("waits for data") is in the
"source" in "work":
On Nov 27, 2005, at 6:24 PM, Eric Blossom wrote:
(1) An audio source should block until it can return some non-zero
amount of data from the underlying audio interface. If it asks for
1024 samples and it gets 64 samples, it should return after the 64
samples, not reissue a blocking call for the remaining 1024-64
samples.
Otherwise, there is no "waiting for data" type of blocking. The
semaphores are used only to keep the various threads from stepping on
each others toes. Here is a little more info:
3) This all started in the "sink", where CoreAudio (CA) uses a thread
and "pulls" data from a callback (which I create), while GR uses a
thread to "push" data to the code I write. Somehow these need to
talk to one another, and the fastest way (within the limits of the GR
programming model and languages and such) is a ring buffer that can
be accessed by both threads. The scenario is similar with the
"source", where CA issues a callback indicating that data from the
input device is ready to be read; this callback reads the data,
converts it to what GR needs, then drops it into another ring buffer
to be read by GR's callback "work".
The primary problem I was having was that, as I cannot control
context switching between threads, it could happen that a variable
would be in the process of being written to by both threads at the
same time. This happened very infrequently, but when it did it would
lead to a buffer underrun (meaning that the code thought there was
more data but there really wasn't).
There was only 1 variable to which this was happening where I could
"see" it (a counter with the current total number of samples stored
in the buffers), but it was clear that I needed a thread-safe
blocking mechanism ... so I asked the question and got the answer
quickly "onmi_semaphores". I implemented those semaphores for both
source and sink, and that took care of the issue. My particular
implementation is very simple for both "sink" and "source": it waits
() at the start of "work" and posts() at the end, and likewise waits
() at the start of the CoreAudio callback and posts() at the end. At
least for audio up to 50 kHz sample rate, this does not seem to cause
any significant issues when feeding either CoreAudio or GR.
For "sink", "work" just copies the buffer then returns, and the CA
callback just takes the copied "work" buffer, copies it to the CA
buffer then returns. On this side, the semaphores cause very little
in the way of increased latency or decreased throughput; while
another implementation might be "better", this one works just fine
for now.
For "source", the CA callback has to get the data from the input
device, convert the data to GR's wants, and copy the output to the
ring buffer. "work" blocks until is has data to return (per Eric's
directive above; releasing the semaphore while blocking to allow for
data to come in from the CA callback) by copying from the ring buffer
to the output variable. On this side, there is probably a more
efficient way to do the semaphores, but what I have works ... and
that's a good place to start!
What I need to do is to pick through the code and determine how to
better implement the semaphores in order to reduce latency and/or
improve throughput, or just to have cleaner code. One option - and
probably the one which makes the most sense - is to augment my ring
buffer to the thread-safe (via internal semaphore use), and remove
the semaphores from my code. This would benefit both the "source"
and "sink", so it's something I might experiment with.
So another question: Does GR have its own ring buffer that is thread-
safe? If so, I'd prefer to use that one rather than muck with my own
code (no matter how simple or not it might be; why reinvent the wheel
every time, no?).