void ZKMORHP_IOThreadSlave::WorkLoopTeardown() { // The other thread holds the lock during teardown try { mWorkLoopPhase = kTeardownPhase; mDevice->GetIOCycleTelemetry().IOCycleTeardownBegin(mIOCycleCounter); // the work loop has finished, clear the time constraints ClearTimeConstraints(); // tell the device that the IO thread is torn down mDevice->StopIOCycleTimingServices(); } catch(const CAException& inException) { DebugMessageN1("ZKMORHP_IOThreadSlave::WorkLoopTeardown: Caught a CAException, code == %ld", (long int)inException.GetError()); } catch(...) { DebugMessage("ZKMORHP_IOThreadSlave::WorkLoopTeardown: Caught an unknown exception."); } // set the device state to know the engine has stopped mDevice->IOEngineStopped(); // Notify clients that the IO thread is stopping CAPropertyAddress theIsRunningAddress(kAudioDevicePropertyDeviceIsRunning); mDevice->PropertiesChanged(1, &theIsRunningAddress); mDevice->GetIOCycleTelemetry().IOCycleTeardownEnd(mIOCycleCounter); mWorkLoopPhase = kNotRunningPhase; mIOGuard.NotifyAll(); mIOCycleCounter = 0; }
void ZKMORHP_IOThreadSlave::WorkLoop() { // grab the IO guard bool wasLocked = mIOGuard.Lock(); // initialize some stuff mWorkLoopPhase = kInitializingPhase; mIOCycleCounter = 0; mOverloadCounter = 0; CAPropertyAddress theIsRunningAddress(kAudioDevicePropertyDeviceIsRunning); mDevice->GetIOCycleTelemetry().IOCycleInitializeBegin(mIOCycleCounter); try { // and signal that the IO thread is running mIOGuard.NotifyAll(); // initialize the work loop stopping conditions mStopWorkLoop = false; // Tell the device that the IO thread has initialized. Note that we unlock around this call // due to the fact that IOCycleInitialize might not return for a while because it might // have to wait for the hardware to start. if(wasLocked) { mIOGuard.Unlock(); } // tell the device that the IO cycle is initializing to start the timing services mDevice->StartIOCycleTimingServices(); // set the device state to know the engine is running mDevice->IOEngineStarted(); // notify clients that the engine is running mDevice->PropertiesChanged(1, &theIsRunningAddress); // re-lock the guard wasLocked = mIOGuard.Lock(); // make sure the thread is still running before moving on if(!mStopWorkLoop) { // set the time constraints for the IOThread SetTimeConstraints(); // initialize the clock mDevice->EstablishIOCycleAnchorTime(mAnchorTime); mFrameCounter = 0; #if Offset_For_Input if(mDevice->HasInputStreams()) { // the first sleep cycle as to be at least the input safety offset and a buffer's // worth of time to be sure that the input data is all there mFrameCounter += mDevice->GetSafetyOffset(true); } #endif // enter the work loop mWorkLoopPhase = kRunningPhase; bool isInNeedOfResynch = false; mDevice->GetIOCycleTelemetry().IOCycleInitializeEnd(mIOCycleCounter, mAnchorTime); while(!mStopWorkLoop) { // get the current time AudioTimeStamp theCurrentTime; mDevice->GetCurrentTime(theCurrentTime); // calculate the next wake up time AudioTimeStamp theNextWakeUpTime = CAAudioTimeStamp::kZero; theNextWakeUpTime.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid; if(CalculateNextWakeUpTime(theCurrentTime, theNextWakeUpTime, isInNeedOfResynch, wasLocked)) { // sleep until the next wake up time mDevice->GetIOCycleTelemetry().IOCycleWorkLoopEnd(mIOCycleCounter, theCurrentTime, theNextWakeUpTime); mIOGuard.WaitUntil(CAHostTimeBase::ConvertToNanos(theNextWakeUpTime.mHostTime)); // increment the counter ++mIOCycleCounter; // do IO if the thread wasn't stopped if(!mStopWorkLoop) { // get the current time mDevice->GetCurrentTime(theCurrentTime); #if Log_SchedulingLatency // check to see if we have incurred a large scheduling latency if(theCurrentTime.mHostTime > (theNextWakeUpTime.mHostTime + mAllowedLatency)) { // log it mLatencyLog->Capture(theNextWakeUpTime.mHostTime - mAllowedLatency, theCurrentTime.mHostTime, true); // print how late we are DebugMessageN1("HP_IOThread::WorkLoop: woke up late by %f milliseconds", ((Float64)CAHostTimeBase::ConvertToNanos(theCurrentTime.mHostTime - theNextWakeUpTime.mHostTime)) / (1000.0 * 1000.0)); } #endif if(theCurrentTime.mSampleTime >= (mAnchorTime.mSampleTime + mFrameCounter)) { // increment the frame counter mFrameCounter += mDevice->GetIOBufferFrameSize(); // the new cycle is starting mDevice->GetIOCycleTelemetry().IOCycleWorkLoopBegin(mIOCycleCounter, theCurrentTime); if(mDevice->UpdateIOCycleTimingServices()) { // something unexpected happenned with the time stamp, so resynch prior to doing IO AudioTimeStamp theNewAnchor = CAAudioTimeStamp::kZero; theNewAnchor.mSampleTime = 0; theNewAnchor.mHostTime = 0; theNewAnchor.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid; if(mDevice->EstablishIOCycleAnchorTime(theNewAnchor)) { Resynch(&theNewAnchor, false); } else { Resynch(NULL, false); } // re-get the current time too mDevice->GetCurrentTime(theCurrentTime); } // do the IO isInNeedOfResynch = PerformIO(theCurrentTime); } } } else { // calculating the next wake up time failed, so we just stop everything (which // will get picked up when the commands are executed mDevice->ClearAllCommands(); mDevice->Do_StopAllIOProcs(); } // execute any deferred commands mDevice->ExecuteAllCommands(); } } mWorkLoopPhase = kTeardownPhase; mDevice->GetIOCycleTelemetry().IOCycleTeardownBegin(mIOCycleCounter); // the work loop has finished, clear the time constraints ClearTimeConstraints(); // tell the device that the IO thread is torn down mDevice->StopIOCycleTimingServices(); } catch(const CAException& inException) { DebugMessageN1("HP_IOThread::WorkLoop: Caught a CAException, code == %ld", (long int)inException.GetError()); } catch(...) { DebugMessage("HP_IOThread::WorkLoop: Caught an unknown exception."); } // set the device state to know the engine has stopped mDevice->IOEngineStopped(); // Notify clients that the IO thread is stopping. Note that we unlock around this call // due to the fact that clients might want to call back into the HAL. if(wasLocked) { mIOGuard.Unlock(); } // Notify clients that the IO thread is stopping mDevice->PropertiesChanged(1, &theIsRunningAddress); // re-lock the guard wasLocked = mIOGuard.Lock(); mDevice->GetIOCycleTelemetry().IOCycleTeardownEnd(mIOCycleCounter); mWorkLoopPhase = kNotRunningPhase; mIOGuard.NotifyAll(); mIOCycleCounter = 0; if(wasLocked) { mIOGuard.Unlock(); } }
void HP_IOThread::WorkLoop() { // grab the IO guard bool wasLocked = mIOGuard.Lock(); // initialize some stuff mWorkLoopPhase = kInitializingPhase; mIOCycleCounter = 0; mOverloadCounter = 0; CAPropertyAddress theIsRunningAddress(kAudioDevicePropertyDeviceIsRunning); #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleInitializeBegin(mIOCycleCounter); #else HAL_IOCYCLEINITIALIZEBEGIN(mIOCycleCounter); #endif try { // and signal that the IO thread is running mIOGuard.NotifyAll(); // initialize the work loop stopping conditions mStopWorkLoop = false; // Tell the device that the IO thread has initialized. Note that we unlock around this call // due to the fact that IOCycleInitialize might not return for a while because it might // have to wait for the hardware to start. if(wasLocked) { mIOGuard.Unlock(); } // tell the device that the IO cycle is initializing to start the timing services mDevice->StartIOCycleTimingServices(); // set the device state to know the engine is running mDevice->IOEngineStarted(); // notify clients that the engine is running mDevice->PropertiesChanged(1, &theIsRunningAddress); // re-lock the guard wasLocked = mIOGuard.Lock(); // make sure the thread is still running before moving on if(!mStopWorkLoop) { // set the time constraints for the IOThread SetTimeConstraints(); // initialize the clock mDevice->EstablishIOCycleAnchorTime(mAnchorTime); mFrameCounter = 0; #if Offset_For_Input if(mDevice->HasInputStreams()) { // the first sleep cycle as to be at least the input safety offset and a buffer's // worth of time to be sure that the input data is all there mFrameCounter += mDevice->GetSafetyOffset(true); } #endif // get the current time AudioTimeStamp theCurrentTime; mDevice->GetCurrentTime(theCurrentTime); // enter the work loop mWorkLoopPhase = kRunningPhase; bool isInNeedOfResynch = false; bool isCheckingForOverloads = false; Float64 theIOBufferFrameSize = mDevice->GetIOBufferFrameSize(); #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleInitializeEnd(mIOCycleCounter, mAnchorTime); mDevice->GetIOCycleTelemetry().IOCycleWorkLoopBegin(mIOCycleCounter, theCurrentTime); #else HAL_IOCYCLEINITIALIZEEND(mIOCycleCounter, mAnchorTime); HAL_IOCYCLEWORKLOOPBEGIN(mIOCycleCounter, theCurrentTime); #endif while(!mStopWorkLoop) { // get the new IO buffer frame size Float64 theNewIOBufferFrameSize = mDevice->GetIOBufferFrameSize(); // initialize the next wake up time AudioTimeStamp theNextWakeUpTime = CAAudioTimeStamp::kZero; theNextWakeUpTime.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid; // get the current time mDevice->GetCurrentTime(theCurrentTime); // we have to run a special, untimed IO cycle if the IO buffer size changed if((theNewIOBufferFrameSize != theIOBufferFrameSize) && (theCurrentTime.mSampleTime >= (mAnchorTime.mSampleTime + mFrameCounter))) { // mark the end of the previous cycle #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleWorkLoopEnd(mIOCycleCounter, theCurrentTime, theNextWakeUpTime); #else HAL_IOCYCLEWORKLOOPEND(mIOCycleCounter, theCurrentTime, theNextWakeUpTime); #endif // increment the cycle counter ++mIOCycleCounter; // increment the frame counter mFrameCounter += theIOBufferFrameSize; // the new cycle is starting #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleWorkLoopBegin(mIOCycleCounter, theCurrentTime); #else HAL_IOCYCLEWORKLOOPBEGIN(mIOCycleCounter, theCurrentTime); #endif // do the IO, note that we don't need to update the timing services for this special cycle isInNeedOfResynch = PerformIO(theCurrentTime, theIOBufferFrameSize); // turn off overload checking for the next cycle to be nice to clients isCheckingForOverloads = false; } // calculate the next wake up time if(CalculateNextWakeUpTime(theCurrentTime, theIOBufferFrameSize, theNextWakeUpTime, isCheckingForOverloads, isInNeedOfResynch, wasLocked)) { // sleep until the next wake up time #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleWorkLoopEnd(mIOCycleCounter, theCurrentTime, theNextWakeUpTime); #else HAL_IOCYCLEWORKLOOPEND(mIOCycleCounter, theCurrentTime, theNextWakeUpTime); #endif mIOGuard.WaitUntil(CAHostTimeBase::ConvertToNanos(theNextWakeUpTime.mHostTime)); // increment the cycle counter ++mIOCycleCounter; // make sure overload checking is enabled isCheckingForOverloads = true; // do IO if the thread wasn't stopped if(!mStopWorkLoop) { // get the current time mDevice->GetCurrentTime(theCurrentTime); if(theCurrentTime.mSampleTime >= (mAnchorTime.mSampleTime + mFrameCounter)) { // increment the frame counter mFrameCounter += theIOBufferFrameSize; // refresh the current buffer size theIOBufferFrameSize = theNewIOBufferFrameSize; // the new cycle is starting #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleWorkLoopBegin(mIOCycleCounter, theCurrentTime); #else HAL_IOCYCLEWORKLOOPBEGIN(mIOCycleCounter, theCurrentTime); #endif if(mDevice->UpdateIOCycleTimingServices()) { // something unexpected happenned with the time stamp, so resynch prior to doing IO AudioTimeStamp theNewAnchor = CAAudioTimeStamp::kZero; theNewAnchor.mSampleTime = 0; theNewAnchor.mHostTime = 0; theNewAnchor.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid; if(mDevice->EstablishIOCycleAnchorTime(theNewAnchor)) { Resynch(&theNewAnchor, false); } else { Resynch(NULL, false); } // re-get the current time too mDevice->GetCurrentTime(theCurrentTime); // mark the telemetry #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().Resynch(GetIOCycleNumber(), mAnchorTime); #else HAL_RESYNCH(GetIOCycleNumber(), mAnchorTime); #endif } // do the IO isInNeedOfResynch = PerformIO(theCurrentTime, theIOBufferFrameSize); } } } else { // calculating the next wake up time failed, so we just stop everything (which // will get picked up when the commands are executed mDevice->ClearAllCommands(); mDevice->Do_StopAllIOProcs(); } // execute any deferred commands mDevice->ExecuteAllCommands(); } } mWorkLoopPhase = kTeardownPhase; #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleTeardownBegin(mIOCycleCounter); #else HAL_IOCYCLETEARDOWNBEGIN(mIOCycleCounter); #endif // the work loop has finished, clear the time constraints ClearTimeConstraints(); // tell the device that the IO thread is torn down mDevice->StopIOCycleTimingServices(); } catch(const CAException& inException) { DebugMessageN1("HP_IOThread::WorkLoop: Caught a CAException, code == %ld", (long int)inException.GetError()); } catch(...) { DebugMessage("HP_IOThread::WorkLoop: Caught an unknown exception."); } // set the device state to know the engine has stopped mDevice->IOEngineStopped(); // Notify clients that the IO thread is stopping. Note that we unlock around this call // due to the fact that clients might want to call back into the HAL. if(wasLocked) { mIOGuard.Unlock(); } // Notify clients that the IO thread is stopping mDevice->PropertiesChanged(1, &theIsRunningAddress); // re-lock the guard wasLocked = mIOGuard.Lock(); #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleTeardownEnd(mIOCycleCounter); #else HAL_IOCYCLETEARDOWNEND(mIOCycleCounter); #endif mWorkLoopPhase = kNotRunningPhase; mIOGuard.NotifyAll(); mIOCycleCounter = 0; if(wasLocked) { mIOGuard.Unlock(); } }