• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "FastThread"
18 //#define LOG_NDEBUG 0
19 
20 #define ATRACE_TAG ATRACE_TAG_AUDIO
21 
22 #include "Configuration.h"
23 #include <linux/futex.h>
24 #include <sys/syscall.h>
25 #include <utils/Log.h>
26 #include <utils/Trace.h>
27 #include "FastThread.h"
28 #include "FastThreadDumpState.h"
29 
30 #define FAST_DEFAULT_NS    999999999L   // ~1 sec: default time to sleep
31 #define FAST_HOT_IDLE_NS     1000000L   // 1 ms: time to sleep while hot idling
32 #define MIN_WARMUP_CYCLES          2    // minimum number of consecutive in-range loop cycles
33                                         // to wait for warmup
34 #define MAX_WARMUP_CYCLES         10    // maximum number of loop cycles to wait for warmup
35 
36 namespace android {
37 
FastThread(const char * cycleMs,const char * loadUs)38 FastThread::FastThread(const char *cycleMs, const char *loadUs) : Thread(false /*canCallJava*/),
39     // re-initialized to &sInitial by subclass constructor
40     mPrevious(NULL), mCurrent(NULL),
41     /* mOldTs({0, 0}), */
42     mOldTsValid(false),
43     mSleepNs(-1),
44     mPeriodNs(0),
45     mUnderrunNs(0),
46     mOverrunNs(0),
47     mForceNs(0),
48     mWarmupNsMin(0),
49     mWarmupNsMax(LONG_MAX),
50     // re-initialized to &mDummySubclassDumpState by subclass constructor
51     mDummyDumpState(NULL),
52     mDumpState(NULL),
53     mIgnoreNextOverrun(true),
54 #ifdef FAST_THREAD_STATISTICS
55     // mOldLoad
56     mOldLoadValid(false),
57     mBounds(0),
58     mFull(false),
59     // mTcu
60 #endif
61     mColdGen(0),
62     mIsWarm(false),
63     /* mMeasuredWarmupTs({0, 0}), */
64     mWarmupCycles(0),
65     mWarmupConsecutiveInRangeCycles(0),
66     // mDummyLogWriter
67     mLogWriter(&mDummyLogWriter),
68     mTimestampStatus(INVALID_OPERATION),
69 
70     mCommand(FastThreadState::INITIAL),
71 #if 0
72     frameCount(0),
73 #endif
74     mAttemptedWrite(false)
75     // mCycleMs(cycleMs)
76     // mLoadUs(loadUs)
77 {
78     mOldTs.tv_sec = 0;
79     mOldTs.tv_nsec = 0;
80     mMeasuredWarmupTs.tv_sec = 0;
81     mMeasuredWarmupTs.tv_nsec = 0;
82     strlcpy(mCycleMs, cycleMs, sizeof(mCycleMs));
83     strlcpy(mLoadUs, loadUs, sizeof(mLoadUs));
84 }
85 
~FastThread()86 FastThread::~FastThread()
87 {
88 }
89 
threadLoop()90 bool FastThread::threadLoop()
91 {
92     for (;;) {
93 
94         // either nanosleep, sched_yield, or busy wait
95         if (mSleepNs >= 0) {
96             if (mSleepNs > 0) {
97                 ALOG_ASSERT(mSleepNs < 1000000000);
98                 const struct timespec req = {0, mSleepNs};
99                 nanosleep(&req, NULL);
100             } else {
101                 sched_yield();
102             }
103         }
104         // default to long sleep for next cycle
105         mSleepNs = FAST_DEFAULT_NS;
106 
107         // poll for state change
108         const FastThreadState *next = poll();
109         if (next == NULL) {
110             // continue to use the default initial state until a real state is available
111             // FIXME &sInitial not available, should save address earlier
112             //ALOG_ASSERT(mCurrent == &sInitial && previous == &sInitial);
113             next = mCurrent;
114         }
115 
116         mCommand = next->mCommand;
117         if (next != mCurrent) {
118 
119             // As soon as possible of learning of a new dump area, start using it
120             mDumpState = next->mDumpState != NULL ? next->mDumpState : mDummyDumpState;
121             mLogWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &mDummyLogWriter;
122             setLog(mLogWriter);
123 
124             // We want to always have a valid reference to the previous (non-idle) state.
125             // However, the state queue only guarantees access to current and previous states.
126             // So when there is a transition from a non-idle state into an idle state, we make a
127             // copy of the last known non-idle state so it is still available on return from idle.
128             // The possible transitions are:
129             //  non-idle -> non-idle    update previous from current in-place
130             //  non-idle -> idle        update previous from copy of current
131             //  idle     -> idle        don't update previous
132             //  idle     -> non-idle    don't update previous
133             if (!(mCurrent->mCommand & FastThreadState::IDLE)) {
134                 if (mCommand & FastThreadState::IDLE) {
135                     onIdle();
136                     mOldTsValid = false;
137 #ifdef FAST_THREAD_STATISTICS
138                     mOldLoadValid = false;
139 #endif
140                     mIgnoreNextOverrun = true;
141                 }
142                 mPrevious = mCurrent;
143             }
144             mCurrent = next;
145         }
146 #if !LOG_NDEBUG
147         next = NULL;    // not referenced again
148 #endif
149 
150         mDumpState->mCommand = mCommand;
151 
152         // FIXME what does this comment mean?
153         // << current, previous, command, dumpState >>
154 
155         switch (mCommand) {
156         case FastThreadState::INITIAL:
157         case FastThreadState::HOT_IDLE:
158             mSleepNs = FAST_HOT_IDLE_NS;
159             continue;
160         case FastThreadState::COLD_IDLE:
161             // only perform a cold idle command once
162             // FIXME consider checking previous state and only perform if previous != COLD_IDLE
163             if (mCurrent->mColdGen != mColdGen) {
164                 int32_t *coldFutexAddr = mCurrent->mColdFutexAddr;
165                 ALOG_ASSERT(coldFutexAddr != NULL);
166                 int32_t old = android_atomic_dec(coldFutexAddr);
167                 if (old <= 0) {
168                     syscall(__NR_futex, coldFutexAddr, FUTEX_WAIT_PRIVATE, old - 1, NULL);
169                 }
170                 int policy = sched_getscheduler(0) & ~SCHED_RESET_ON_FORK;
171                 if (!(policy == SCHED_FIFO || policy == SCHED_RR)) {
172                     ALOGE("did not receive expected priority boost");
173                 }
174                 // This may be overly conservative; there could be times that the normal mixer
175                 // requests such a brief cold idle that it doesn't require resetting this flag.
176                 mIsWarm = false;
177                 mMeasuredWarmupTs.tv_sec = 0;
178                 mMeasuredWarmupTs.tv_nsec = 0;
179                 mWarmupCycles = 0;
180                 mWarmupConsecutiveInRangeCycles = 0;
181                 mSleepNs = -1;
182                 mColdGen = mCurrent->mColdGen;
183 #ifdef FAST_THREAD_STATISTICS
184                 mBounds = 0;
185                 mFull = false;
186 #endif
187                 mOldTsValid = !clock_gettime(CLOCK_MONOTONIC, &mOldTs);
188                 mTimestampStatus = INVALID_OPERATION;
189             } else {
190                 mSleepNs = FAST_HOT_IDLE_NS;
191             }
192             continue;
193         case FastThreadState::EXIT:
194             onExit();
195             return false;
196         default:
197             LOG_ALWAYS_FATAL_IF(!isSubClassCommand(mCommand));
198             break;
199         }
200 
201         // there is a non-idle state available to us; did the state change?
202         if (mCurrent != mPrevious) {
203             onStateChange();
204 #if 1   // FIXME shouldn't need this
205             // only process state change once
206             mPrevious = mCurrent;
207 #endif
208         }
209 
210         // do work using current state here
211         mAttemptedWrite = false;
212         onWork();
213 
214         // To be exactly periodic, compute the next sleep time based on current time.
215         // This code doesn't have long-term stability when the sink is non-blocking.
216         // FIXME To avoid drift, use the local audio clock or watch the sink's fill status.
217         struct timespec newTs;
218         int rc = clock_gettime(CLOCK_MONOTONIC, &newTs);
219         if (rc == 0) {
220             //mLogWriter->logTimestamp(newTs);
221             if (mOldTsValid) {
222                 time_t sec = newTs.tv_sec - mOldTs.tv_sec;
223                 long nsec = newTs.tv_nsec - mOldTs.tv_nsec;
224                 ALOGE_IF(sec < 0 || (sec == 0 && nsec < 0),
225                         "clock_gettime(CLOCK_MONOTONIC) failed: was %ld.%09ld but now %ld.%09ld",
226                         mOldTs.tv_sec, mOldTs.tv_nsec, newTs.tv_sec, newTs.tv_nsec);
227                 if (nsec < 0) {
228                     --sec;
229                     nsec += 1000000000;
230                 }
231                 // To avoid an initial underrun on fast tracks after exiting standby,
232                 // do not start pulling data from tracks and mixing until warmup is complete.
233                 // Warmup is considered complete after the earlier of:
234                 //      MIN_WARMUP_CYCLES consecutive in-range write() attempts,
235                 //          where "in-range" means mWarmupNsMin <= cycle time <= mWarmupNsMax
236                 //      MAX_WARMUP_CYCLES write() attempts.
237                 // This is overly conservative, but to get better accuracy requires a new HAL API.
238                 if (!mIsWarm && mAttemptedWrite) {
239                     mMeasuredWarmupTs.tv_sec += sec;
240                     mMeasuredWarmupTs.tv_nsec += nsec;
241                     if (mMeasuredWarmupTs.tv_nsec >= 1000000000) {
242                         mMeasuredWarmupTs.tv_sec++;
243                         mMeasuredWarmupTs.tv_nsec -= 1000000000;
244                     }
245                     ++mWarmupCycles;
246                     if (mWarmupNsMin <= nsec && nsec <= mWarmupNsMax) {
247                         ALOGV("warmup cycle %d in range: %.03f ms", mWarmupCycles, nsec * 1e-9);
248                         ++mWarmupConsecutiveInRangeCycles;
249                     } else {
250                         ALOGV("warmup cycle %d out of range: %.03f ms", mWarmupCycles, nsec * 1e-9);
251                         mWarmupConsecutiveInRangeCycles = 0;
252                     }
253                     if ((mWarmupConsecutiveInRangeCycles >= MIN_WARMUP_CYCLES) ||
254                             (mWarmupCycles >= MAX_WARMUP_CYCLES)) {
255                         mIsWarm = true;
256                         mDumpState->mMeasuredWarmupTs = mMeasuredWarmupTs;
257                         mDumpState->mWarmupCycles = mWarmupCycles;
258                     }
259                 }
260                 mSleepNs = -1;
261                 if (mIsWarm) {
262                     if (sec > 0 || nsec > mUnderrunNs) {
263                         ATRACE_NAME("underrun");
264                         // FIXME only log occasionally
265                         ALOGV("underrun: time since last cycle %d.%03ld sec",
266                                 (int) sec, nsec / 1000000L);
267                         mDumpState->mUnderruns++;
268                         mIgnoreNextOverrun = true;
269                     } else if (nsec < mOverrunNs) {
270                         if (mIgnoreNextOverrun) {
271                             mIgnoreNextOverrun = false;
272                         } else {
273                             // FIXME only log occasionally
274                             ALOGV("overrun: time since last cycle %d.%03ld sec",
275                                     (int) sec, nsec / 1000000L);
276                             mDumpState->mOverruns++;
277                         }
278                         // This forces a minimum cycle time. It:
279                         //  - compensates for an audio HAL with jitter due to sample rate conversion
280                         //  - works with a variable buffer depth audio HAL that never pulls at a
281                         //    rate < than mOverrunNs per buffer.
282                         //  - recovers from overrun immediately after underrun
283                         // It doesn't work with a non-blocking audio HAL.
284                         mSleepNs = mForceNs - nsec;
285                     } else {
286                         mIgnoreNextOverrun = false;
287                     }
288                 }
289 #ifdef FAST_THREAD_STATISTICS
290                 if (mIsWarm) {
291                     // advance the FIFO queue bounds
292                     size_t i = mBounds & (mDumpState->mSamplingN - 1);
293                     mBounds = (mBounds & 0xFFFF0000) | ((mBounds + 1) & 0xFFFF);
294                     if (mFull) {
295                         mBounds += 0x10000;
296                     } else if (!(mBounds & (mDumpState->mSamplingN - 1))) {
297                         mFull = true;
298                     }
299                     // compute the delta value of clock_gettime(CLOCK_MONOTONIC)
300                     uint32_t monotonicNs = nsec;
301                     if (sec > 0 && sec < 4) {
302                         monotonicNs += sec * 1000000000;
303                     }
304                     // compute raw CPU load = delta value of clock_gettime(CLOCK_THREAD_CPUTIME_ID)
305                     uint32_t loadNs = 0;
306                     struct timespec newLoad;
307                     rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &newLoad);
308                     if (rc == 0) {
309                         if (mOldLoadValid) {
310                             sec = newLoad.tv_sec - mOldLoad.tv_sec;
311                             nsec = newLoad.tv_nsec - mOldLoad.tv_nsec;
312                             if (nsec < 0) {
313                                 --sec;
314                                 nsec += 1000000000;
315                             }
316                             loadNs = nsec;
317                             if (sec > 0 && sec < 4) {
318                                 loadNs += sec * 1000000000;
319                             }
320                         } else {
321                             // first time through the loop
322                             mOldLoadValid = true;
323                         }
324                         mOldLoad = newLoad;
325                     }
326 #ifdef CPU_FREQUENCY_STATISTICS
327                     // get the absolute value of CPU clock frequency in kHz
328                     int cpuNum = sched_getcpu();
329                     uint32_t kHz = mTcu.getCpukHz(cpuNum);
330                     kHz = (kHz << 4) | (cpuNum & 0xF);
331 #endif
332                     // save values in FIFO queues for dumpsys
333                     // these stores #1, #2, #3 are not atomic with respect to each other,
334                     // or with respect to store #4 below
335                     mDumpState->mMonotonicNs[i] = monotonicNs;
336                     mDumpState->mLoadNs[i] = loadNs;
337 #ifdef CPU_FREQUENCY_STATISTICS
338                     mDumpState->mCpukHz[i] = kHz;
339 #endif
340                     // this store #4 is not atomic with respect to stores #1, #2, #3 above, but
341                     // the newest open & oldest closed halves are atomic with respect to each other
342                     mDumpState->mBounds = mBounds;
343                     ATRACE_INT(mCycleMs, monotonicNs / 1000000);
344                     ATRACE_INT(mLoadUs, loadNs / 1000);
345                 }
346 #endif
347             } else {
348                 // first time through the loop
349                 mOldTsValid = true;
350                 mSleepNs = mPeriodNs;
351                 mIgnoreNextOverrun = true;
352             }
353             mOldTs = newTs;
354         } else {
355             // monotonic clock is broken
356             mOldTsValid = false;
357             mSleepNs = mPeriodNs;
358         }
359 
360     }   // for (;;)
361 
362     // never return 'true'; Thread::_threadLoop() locks mutex which can result in priority inversion
363 }
364 
365 }   // namespace android
366