Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

[osxsink/iossink] - don't block addpackets if ca didn't do the initial d... #4322

Merged
merged 1 commit into from

6 participants

Memphiz davilla jmarshallnz Rainer Hochecker Trent Nelson Martijn Kaijser
Memphiz
Owner

...ata pull - but timeout gracefully for signaling an error condition to the engine.

This should fix a race/deadlock when unplugging cables/switching of amp/tv ...

Memphiz Memphiz added the Gotham label
Memphiz
Owner

batman build this please (this is a jenkins test trigger for merging this pr into the Gotham branch instead of master ...)

xbmc/cores/AudioEngine/Sinks/AESinkDARWINIOS.cpp
((12 lines not shown))
else
- condVar.wait(lock, 900 * frames / m_sampleRate);
+ signaled = condVar.wait(lock, 900 * frames / m_sampleRate);
jmarshallnz Owner

Not sure if we want to signal a failure here - a failure here is not terminal as we're specifically waiting less than the number of frames, which isn't really a problem. At worst we return 0 here.

IMO the INT_MAX should only be done in the !m_started branch.

Another thing is how will spurious wakeups come into it? Are they uncommon enough that we don't need to worry about it? At worse, what happens when we return INT_MAX: Is the sink reinit'd ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
davilla
Collaborator

spurious wakeups ? I thought we dealt with those many years ago.

jmarshallnz
Owner

This is using a ConditionalVariable, not CEvent (so as to not muck around with locks in the CA callbacks) which doesn't take care of wake ups I think.

I doubt it's an issue, but a couple of suggestions would be:
1. Change the timeout to 100ms rather than 500ms.
2. Return write_frames rather than INT_MAX.
3. Rely on AEActiveSink calling us 5 times, thus totalling 500ms before it signals an error.

(This is assuming that returning INT_MAX in the case we get a spurious wakeup is a problem - if not, then fair enough, just return INT_MAX in that case only).

Memphiz
Owner

@fritsch or @FernetMenta any comment on spurious wakeup? ^^

@jmarshallnz will change the m_started branch to not signal an error but return write_frames as before.

Rainer Hochecker
Collaborator

the condition variable is slightly abused because there is no predicate to check. In this case spurious wakeups should be considered possible,

Rainer Hochecker
Collaborator

after having discussed this with fritsch I think it is best to add a timer to the sink. reset the timer when data could be delivered to the ringbuffer and signal error (INT_MAX) after time X. probability is low but there may be 5 spurious wakeups in a row.

Memphiz
Owner

Updated - doesn't make much sense to me - but that is what i understood from the irc (which was disturbed by a lengthish "no time" by me :( ). Hope this thingy won't result in a bubble gum until i really get the approach here.

xbmc/cores/AudioEngine/Sinks/AESinkDARWINIOS.cpp
((17 lines not shown))
else
- condVar.wait(lock, 900 * frames / m_sampleRate);
+ signaled = condVar.wait(lock, 900 * frames / m_sampleRate);
Rainer Hochecker Collaborator

I would drop signaled and use the timer here too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Memphiz
Owner

jenkins build this please

xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.cpp
((5 lines not shown))
if (!m_started)
- condVar.wait(lock);
- else
- condVar.wait(lock, 900 * frames / m_format.m_sampleRate);
+ timeout = 500;
+
+ // we are using a timer here for beeing sure for timeouts
+ // condvar can be woken spuriously as signaled
+ XbmcThreads::EndTime timer(timeout);
+ condVar.wait(lock, timeout);
+ if (timer.IsTimePast())
+ return INT_MAX;
jmarshallnz Owner

When m_started is true, we're waiting less time than we are feeding data (900 * frames / m_format.m_sampleRate = 0.9*data_in_seconds). This has the potential of timing out even though we could afford to wait a bit longer (it's unlikely, but could happen?) Unless we know that this branch is causing problems, I'd tend to not return an error in that case - we're waiting only 10-11ms in this branch normally. Thus, I'd either increase the time we wait or drop the error if m_started is true.

Memphiz Owner
Memphiz added a note

yeah it was a suggestion from fernetmenta to use the timer there too ... but we shouldn't fix whats not broken ... i'm with you here ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Memphiz Memphiz [osxsink/iossink] - don't block addpackets if ca didn't do the initia…
…l data pull - but timeout gracefully for signaling an error condition to the engine (use a timer for detecting a real timeout as condvar can be signaled by spurious interrupts)
ecd891c
Memphiz
Owner

updated

jmarshallnz
Owner

jenkins build this please

jmarshallnz jmarshallnz merged commit 7d1eb2d into from
Memphiz
Owner

@jmarshallnz please wait with the gotham backport - i want to wait for user feedback first ... (testbuild in the works)

Memphiz
Owner

btw i will take care of proper PR to gotham branch for my pull requests - no need for you to cherry-pick .

Memphiz
Owner
Rainer Hochecker
Collaborator

the thread is either blocked in AddPackets, Drain, or Deinitialize. can you add some debug logs at entry and exit of the functions and have the use test again?

Rainer Hochecker
Collaborator

If OSX's audio thread stops reading from the buffer, we loop forever in Drain.:
https://github.com/xbmc/xbmc/blob/master/xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.cpp#L647

Memphiz
Owner

I love you for spotting our bugs so quickly :D

Rainer Hochecker
Collaborator

the condVar is not the issue. bytes never gets 0

Memphiz
Owner

Yes i know. I just realized that we use the mutex for condvar.wait and on the other link i posted we use the singlelock for condvar.wait (this is something i just spotted and now i don't know which one is correct or if it doesn't matter at all).

The real fix is here:

Memphiz@593c763

Doing a test build and let the user test before doing a PR ...

Rainer Hochecker
Collaborator

you should pass mutex, not the singlelock

Martijn Kaijser MartijnKaijser added this to the H* 14.0-alpha1 milestone
jmarshallnz jmarshallnz removed the Gotham label
Trent Nelson

@Memphiz

@jmarshallnz please wait with the gotham backport - i want to wait for user feedback first ... (testbuild in the works)

So?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 6, 2014
  1. Memphiz

    [osxsink/iossink] - don't block addpackets if ca didn't do the initia…

    Memphiz authored
    …l data pull - but timeout gracefully for signaling an error condition to the engine (use a timer for detecting a real timeout as condvar can be signaled by spurious interrupts)
This page is out of date. Refresh to see the latest.
12 xbmc/cores/AudioEngine/Sinks/AESinkDARWINIOS.cpp
View
@@ -237,10 +237,16 @@ unsigned int CAAudioUnitSink::write(uint8_t *data, unsigned int frames)
if (m_buffer->GetWriteSize() < frames * m_frameSize)
{ // no space to write - wait for a bit
CSingleLock lock(mutex);
+ unsigned int timeout = 900 * frames / m_sampleRate;
if (!m_started)
- condVar.wait(lock);
- else
- condVar.wait(lock, 900 * frames / m_sampleRate);
+ timeout = 500;
+
+ // we are using a timer here for beeing sure for timeouts
+ // condvar can be woken spuriously as signaled
+ XbmcThreads::EndTime timer(timeout);
+ condVar.wait(lock, timeout);
+ if (!m_started && timer.IsTimePast())
+ return INT_MAX;
}
unsigned int write_frames = std::min(frames, m_buffer->GetWriteSize() / m_frameSize);
12 xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.cpp
View
@@ -561,10 +561,16 @@ unsigned int CAESinkDARWINOSX::AddPackets(uint8_t *data, unsigned int frames, bo
if (m_buffer->GetWriteSize() < frames * m_format.m_frameSize)
{ // no space to write - wait for a bit
CSingleLock lock(mutex);
+ unsigned int timeout = 900 * frames / m_format.m_sampleRate;
if (!m_started)
- condVar.wait(lock);
- else
- condVar.wait(lock, 900 * frames / m_format.m_sampleRate);
+ timeout = 500;
+
+ // we are using a timer here for beeing sure for timeouts
+ // condvar can be woken spuriously as signaled
+ XbmcThreads::EndTime timer(timeout);
+ condVar.wait(lock, timeout);
+ if (!m_started && timer.IsTimePast())
+ return INT_MAX;
}
unsigned int write_frames = std::min(frames, m_buffer->GetWriteSize() / m_format.m_frameSize);
Something went wrong with that request. Please try again.