There were two causes for the slowness:
When thread was paused, it used nanosleep and sleep. These usually
run to completion (except for POSIX signal, which we avoid because it
is low-level). Instead, replace the nanosleep and sleep by condition
timed wait, as that can be made to return early by a condition signal.
Another advantage of condition timed wait is that a condition wait was
already being used at top of thread loop, so it is a simpler change.
The AudioRecord destructor was missing a proxy interrupt that was correct
in AudioTrack. This proxy interrupt is needed in case another thread
is blocked in proxy obtainBuffer.
Does not address the 1 second polling for NS_WHENEVER.
Bug:
10822765
Change-Id: Id665994551e87e4d7da9c7b015f424fd7a0b5560
void pause(); // suspend thread from execution at next loop boundary
void resume(); // allow thread to execute, if not requested to exit
- void pauseConditional();
- // like pause(), but only if prior resume() wasn't latched
private:
+ void pauseInternal(nsecs_t ns = 0LL);
+ // like pause(), but only used internally within thread
+
friend class AudioRecord;
virtual bool threadLoop();
AudioRecord& mReceiver;
virtual ~AudioRecordThread();
Mutex mMyLock; // Thread::mLock is private
Condition mMyCond; // Thread::mThreadExitedCondition is private
- bool mPaused; // whether thread is currently paused
- bool mResumeLatch; // whether next pauseConditional() will be a nop
+ bool mPaused; // whether thread is requested to pause at next loop entry
+ bool mPausedInt; // whether thread internally requests pause
+ nsecs_t mPausedNs; // if mPausedInt then associated timeout, otherwise ignored
};
// body of AudioRecordThread::threadLoop()
void pause(); // suspend thread from execution at next loop boundary
void resume(); // allow thread to execute, if not requested to exit
- void pauseConditional();
- // like pause(), but only if prior resume() wasn't latched
private:
+ void pauseInternal(nsecs_t ns = 0LL);
+ // like pause(), but only used internally within thread
+
friend class AudioTrack;
virtual bool threadLoop();
AudioTrack& mReceiver;
virtual ~AudioTrackThread();
Mutex mMyLock; // Thread::mLock is private
Condition mMyCond; // Thread::mThreadExitedCondition is private
- bool mPaused; // whether thread is currently paused
- bool mResumeLatch; // whether next pauseConditional() will be a nop
+ bool mPaused; // whether thread is requested to pause at next loop entry
+ bool mPausedInt; // whether thread internally requests pause
+ nsecs_t mPausedNs; // if mPausedInt then associated timeout, otherwise ignored
};
// body of AudioTrackThread::threadLoop()
// Otherwise the callback thread will never exit.
stop();
if (mAudioRecordThread != 0) {
+ mProxy->interrupt();
mAudioRecordThread->requestExit(); // see comment in AudioRecord.h
mAudioRecordThread->requestExitAndWait();
mAudioRecordThread.clear();
// =========================================================================
AudioRecord::AudioRecordThread::AudioRecordThread(AudioRecord& receiver, bool bCanCallJava)
- : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mResumeLatch(false)
+ : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL)
{
}
// caller will check for exitPending()
return true;
}
+ if (mPausedInt) {
+ mPausedInt = false;
+ if (mPausedNs > 0) {
+ (void) mMyCond.waitRelative(mMyLock, mPausedNs);
+ } else {
+ mMyCond.wait(mMyLock);
+ }
+ return true;
+ }
}
nsecs_t ns = mReceiver.processAudioBuffer(this);
switch (ns) {
case 0:
return true;
- case NS_WHENEVER:
- sleep(1);
- return true;
case NS_INACTIVE:
- pauseConditional();
+ pauseInternal();
return true;
case NS_NEVER:
return false;
+ case NS_WHENEVER:
+ // FIXME increase poll interval, or make event-driven
+ ns = 1000000000LL;
+ // fall through
default:
LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %lld", ns);
- struct timespec req;
- req.tv_sec = ns / 1000000000LL;
- req.tv_nsec = ns % 1000000000LL;
- nanosleep(&req, NULL /*rem*/);
+ pauseInternal(ns);
return true;
}
}
{
// must be in this order to avoid a race condition
Thread::requestExit();
- resume();
+ AutoMutex _l(mMyLock);
+ if (mPaused || mPausedInt) {
+ mPaused = false;
+ mPausedInt = false;
+ mMyCond.signal();
+ }
}
void AudioRecord::AudioRecordThread::pause()
{
AutoMutex _l(mMyLock);
mPaused = true;
- mResumeLatch = false;
-}
-
-void AudioRecord::AudioRecordThread::pauseConditional()
-{
- AutoMutex _l(mMyLock);
- if (mResumeLatch) {
- mResumeLatch = false;
- } else {
- mPaused = true;
- }
}
void AudioRecord::AudioRecordThread::resume()
AutoMutex _l(mMyLock);
if (mPaused) {
mPaused = false;
- mResumeLatch = false;
mMyCond.signal();
- } else {
- mResumeLatch = true;
}
}
+void AudioRecord::AudioRecordThread::pauseInternal(nsecs_t ns)
+{
+ AutoMutex _l(mMyLock);
+ mPausedInt = true;
+ mPausedNs = ns;
+}
+
// -------------------------------------------------------------------------
}; // namespace android
// =========================================================================
AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava)
- : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mResumeLatch(false)
+ : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL)
{
}
// caller will check for exitPending()
return true;
}
+ if (mPausedInt) {
+ mPausedInt = false;
+ if (mPausedNs > 0) {
+ (void) mMyCond.waitRelative(mMyLock, mPausedNs);
+ } else {
+ mMyCond.wait(mMyLock);
+ }
+ return true;
+ }
}
nsecs_t ns = mReceiver.processAudioBuffer(this);
switch (ns) {
case 0:
return true;
- case NS_WHENEVER:
- sleep(1);
- return true;
case NS_INACTIVE:
- pauseConditional();
+ pauseInternal();
return true;
case NS_NEVER:
return false;
+ case NS_WHENEVER:
+ // FIXME increase poll interval, or make event-driven
+ ns = 1000000000LL;
+ // fall through
default:
LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %lld", ns);
- struct timespec req;
- req.tv_sec = ns / 1000000000LL;
- req.tv_nsec = ns % 1000000000LL;
- nanosleep(&req, NULL /*rem*/);
+ pauseInternal(ns);
return true;
}
}
{
// must be in this order to avoid a race condition
Thread::requestExit();
- resume();
+ AutoMutex _l(mMyLock);
+ if (mPaused || mPausedInt) {
+ mPaused = false;
+ mPausedInt = false;
+ mMyCond.signal();
+ }
}
void AudioTrack::AudioTrackThread::pause()
{
AutoMutex _l(mMyLock);
mPaused = true;
- mResumeLatch = false;
-}
-
-void AudioTrack::AudioTrackThread::pauseConditional()
-{
- AutoMutex _l(mMyLock);
- if (mResumeLatch) {
- mResumeLatch = false;
- } else {
- mPaused = true;
- }
}
void AudioTrack::AudioTrackThread::resume()
AutoMutex _l(mMyLock);
if (mPaused) {
mPaused = false;
- mResumeLatch = false;
mMyCond.signal();
- } else {
- mResumeLatch = true;
}
}
+void AudioTrack::AudioTrackThread::pauseInternal(nsecs_t ns)
+{
+ AutoMutex _l(mMyLock);
+ mPausedInt = true;
+ mPausedNs = ns;
+}
+
}; // namespace android