• Home
  • History
  • Annotate
  • 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 
29 #define FAST_DEFAULT_NS    999999999L   // ~1 sec: default time to sleep
30 #define FAST_HOT_IDLE_NS     1000000L   // 1 ms: time to sleep while hot idling
31 #define MIN_WARMUP_CYCLES          2    // minimum number of loop cycles to wait for warmup
32 #define MAX_WARMUP_CYCLES         10    // maximum number of loop cycles to wait for warmup
33 
34 namespace android {
35 
FastThread()36 FastThread::FastThread() : Thread(false /*canCallJava*/),
37     // re-initialized to &initial by subclass constructor
38      previous(NULL), current(NULL),
39     /* oldTs({0, 0}), */
40     oldTsValid(false),
41     sleepNs(-1),
42     periodNs(0),
43     underrunNs(0),
44     overrunNs(0),
45     forceNs(0),
46     warmupNs(0),
47     // re-initialized to &dummyDumpState by subclass constructor
48     mDummyDumpState(NULL),
49     dumpState(NULL),
50     ignoreNextOverrun(true),
51 #ifdef FAST_MIXER_STATISTICS
52     // oldLoad
53     oldLoadValid(false),
54     bounds(0),
55     full(false),
56     // tcu
57 #endif
58     coldGen(0),
59     isWarm(false),
60     /* measuredWarmupTs({0, 0}), */
61     warmupCycles(0),
62     // dummyLogWriter
63     logWriter(&dummyLogWriter),
64     timestampStatus(INVALID_OPERATION),
65 
66     command(FastThreadState::INITIAL),
67 #if 0
68     frameCount(0),
69 #endif
70     attemptedWrite(false)
71 {
72     oldTs.tv_sec = 0;
73     oldTs.tv_nsec = 0;
74     measuredWarmupTs.tv_sec = 0;
75     measuredWarmupTs.tv_nsec = 0;
76 }
77 
~FastThread()78 FastThread::~FastThread()
79 {
80 }
81 
threadLoop()82 bool FastThread::threadLoop()
83 {
84     for (;;) {
85 
86         // either nanosleep, sched_yield, or busy wait
87         if (sleepNs >= 0) {
88             if (sleepNs > 0) {
89                 ALOG_ASSERT(sleepNs < 1000000000);
90                 const struct timespec req = {0, sleepNs};
91                 nanosleep(&req, NULL);
92             } else {
93                 sched_yield();
94             }
95         }
96         // default to long sleep for next cycle
97         sleepNs = FAST_DEFAULT_NS;
98 
99         // poll for state change
100         const FastThreadState *next = poll();
101         if (next == NULL) {
102             // continue to use the default initial state until a real state is available
103             // FIXME &initial not available, should save address earlier
104             //ALOG_ASSERT(current == &initial && previous == &initial);
105             next = current;
106         }
107 
108         command = next->mCommand;
109         if (next != current) {
110 
111             // As soon as possible of learning of a new dump area, start using it
112             dumpState = next->mDumpState != NULL ? next->mDumpState : mDummyDumpState;
113             logWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &dummyLogWriter;
114             setLog(logWriter);
115 
116             // We want to always have a valid reference to the previous (non-idle) state.
117             // However, the state queue only guarantees access to current and previous states.
118             // So when there is a transition from a non-idle state into an idle state, we make a
119             // copy of the last known non-idle state so it is still available on return from idle.
120             // The possible transitions are:
121             //  non-idle -> non-idle    update previous from current in-place
122             //  non-idle -> idle        update previous from copy of current
123             //  idle     -> idle        don't update previous
124             //  idle     -> non-idle    don't update previous
125             if (!(current->mCommand & FastThreadState::IDLE)) {
126                 if (command & FastThreadState::IDLE) {
127                     onIdle();
128                     oldTsValid = false;
129 #ifdef FAST_MIXER_STATISTICS
130                     oldLoadValid = false;
131 #endif
132                     ignoreNextOverrun = true;
133                 }
134                 previous = current;
135             }
136             current = next;
137         }
138 #if !LOG_NDEBUG
139         next = NULL;    // not referenced again
140 #endif
141 
142         dumpState->mCommand = command;
143 
144         // << current, previous, command, dumpState >>
145 
146         switch (command) {
147         case FastThreadState::INITIAL:
148         case FastThreadState::HOT_IDLE:
149             sleepNs = FAST_HOT_IDLE_NS;
150             continue;
151         case FastThreadState::COLD_IDLE:
152             // only perform a cold idle command once
153             // FIXME consider checking previous state and only perform if previous != COLD_IDLE
154             if (current->mColdGen != coldGen) {
155                 int32_t *coldFutexAddr = current->mColdFutexAddr;
156                 ALOG_ASSERT(coldFutexAddr != NULL);
157                 int32_t old = android_atomic_dec(coldFutexAddr);
158                 if (old <= 0) {
159                     syscall(__NR_futex, coldFutexAddr, FUTEX_WAIT_PRIVATE, old - 1, NULL);
160                 }
161                 int policy = sched_getscheduler(0);
162                 if (!(policy == SCHED_FIFO || policy == SCHED_RR)) {
163                     ALOGE("did not receive expected priority boost");
164                 }
165                 // This may be overly conservative; there could be times that the normal mixer
166                 // requests such a brief cold idle that it doesn't require resetting this flag.
167                 isWarm = false;
168                 measuredWarmupTs.tv_sec = 0;
169                 measuredWarmupTs.tv_nsec = 0;
170                 warmupCycles = 0;
171                 sleepNs = -1;
172                 coldGen = current->mColdGen;
173 #ifdef FAST_MIXER_STATISTICS
174                 bounds = 0;
175                 full = false;
176 #endif
177                 oldTsValid = !clock_gettime(CLOCK_MONOTONIC, &oldTs);
178                 timestampStatus = INVALID_OPERATION;
179             } else {
180                 sleepNs = FAST_HOT_IDLE_NS;
181             }
182             continue;
183         case FastThreadState::EXIT:
184             onExit();
185             return false;
186         default:
187             LOG_ALWAYS_FATAL_IF(!isSubClassCommand(command));
188             break;
189         }
190 
191         // there is a non-idle state available to us; did the state change?
192         if (current != previous) {
193             onStateChange();
194 #if 1   // FIXME shouldn't need this
195             // only process state change once
196             previous = current;
197 #endif
198         }
199 
200         // do work using current state here
201         attemptedWrite = false;
202         onWork();
203 
204         // To be exactly periodic, compute the next sleep time based on current time.
205         // This code doesn't have long-term stability when the sink is non-blocking.
206         // FIXME To avoid drift, use the local audio clock or watch the sink's fill status.
207         struct timespec newTs;
208         int rc = clock_gettime(CLOCK_MONOTONIC, &newTs);
209         if (rc == 0) {
210             //logWriter->logTimestamp(newTs);
211             if (oldTsValid) {
212                 time_t sec = newTs.tv_sec - oldTs.tv_sec;
213                 long nsec = newTs.tv_nsec - oldTs.tv_nsec;
214                 ALOGE_IF(sec < 0 || (sec == 0 && nsec < 0),
215                         "clock_gettime(CLOCK_MONOTONIC) failed: was %ld.%09ld but now %ld.%09ld",
216                         oldTs.tv_sec, oldTs.tv_nsec, newTs.tv_sec, newTs.tv_nsec);
217                 if (nsec < 0) {
218                     --sec;
219                     nsec += 1000000000;
220                 }
221                 // To avoid an initial underrun on fast tracks after exiting standby,
222                 // do not start pulling data from tracks and mixing until warmup is complete.
223                 // Warmup is considered complete after the earlier of:
224                 //      MIN_WARMUP_CYCLES write() attempts and last one blocks for at least warmupNs
225                 //      MAX_WARMUP_CYCLES write() attempts.
226                 // This is overly conservative, but to get better accuracy requires a new HAL API.
227                 if (!isWarm && attemptedWrite) {
228                     measuredWarmupTs.tv_sec += sec;
229                     measuredWarmupTs.tv_nsec += nsec;
230                     if (measuredWarmupTs.tv_nsec >= 1000000000) {
231                         measuredWarmupTs.tv_sec++;
232                         measuredWarmupTs.tv_nsec -= 1000000000;
233                     }
234                     ++warmupCycles;
235                     if ((nsec > warmupNs && warmupCycles >= MIN_WARMUP_CYCLES) ||
236                             (warmupCycles >= MAX_WARMUP_CYCLES)) {
237                         isWarm = true;
238                         dumpState->mMeasuredWarmupTs = measuredWarmupTs;
239                         dumpState->mWarmupCycles = warmupCycles;
240                     }
241                 }
242                 sleepNs = -1;
243                 if (isWarm) {
244                     if (sec > 0 || nsec > underrunNs) {
245                         ATRACE_NAME("underrun");
246                         // FIXME only log occasionally
247                         ALOGV("underrun: time since last cycle %d.%03ld sec",
248                                 (int) sec, nsec / 1000000L);
249                         dumpState->mUnderruns++;
250                         ignoreNextOverrun = true;
251                     } else if (nsec < overrunNs) {
252                         if (ignoreNextOverrun) {
253                             ignoreNextOverrun = false;
254                         } else {
255                             // FIXME only log occasionally
256                             ALOGV("overrun: time since last cycle %d.%03ld sec",
257                                     (int) sec, nsec / 1000000L);
258                             dumpState->mOverruns++;
259                         }
260                         // This forces a minimum cycle time. It:
261                         //  - compensates for an audio HAL with jitter due to sample rate conversion
262                         //  - works with a variable buffer depth audio HAL that never pulls at a
263                         //    rate < than overrunNs per buffer.
264                         //  - recovers from overrun immediately after underrun
265                         // It doesn't work with a non-blocking audio HAL.
266                         sleepNs = forceNs - nsec;
267                     } else {
268                         ignoreNextOverrun = false;
269                     }
270                 }
271 #ifdef FAST_MIXER_STATISTICS
272                 if (isWarm) {
273                     // advance the FIFO queue bounds
274                     size_t i = bounds & (dumpState->mSamplingN - 1);
275                     bounds = (bounds & 0xFFFF0000) | ((bounds + 1) & 0xFFFF);
276                     if (full) {
277                         bounds += 0x10000;
278                     } else if (!(bounds & (dumpState->mSamplingN - 1))) {
279                         full = true;
280                     }
281                     // compute the delta value of clock_gettime(CLOCK_MONOTONIC)
282                     uint32_t monotonicNs = nsec;
283                     if (sec > 0 && sec < 4) {
284                         monotonicNs += sec * 1000000000;
285                     }
286                     // compute raw CPU load = delta value of clock_gettime(CLOCK_THREAD_CPUTIME_ID)
287                     uint32_t loadNs = 0;
288                     struct timespec newLoad;
289                     rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &newLoad);
290                     if (rc == 0) {
291                         if (oldLoadValid) {
292                             sec = newLoad.tv_sec - oldLoad.tv_sec;
293                             nsec = newLoad.tv_nsec - oldLoad.tv_nsec;
294                             if (nsec < 0) {
295                                 --sec;
296                                 nsec += 1000000000;
297                             }
298                             loadNs = nsec;
299                             if (sec > 0 && sec < 4) {
300                                 loadNs += sec * 1000000000;
301                             }
302                         } else {
303                             // first time through the loop
304                             oldLoadValid = true;
305                         }
306                         oldLoad = newLoad;
307                     }
308 #ifdef CPU_FREQUENCY_STATISTICS
309                     // get the absolute value of CPU clock frequency in kHz
310                     int cpuNum = sched_getcpu();
311                     uint32_t kHz = tcu.getCpukHz(cpuNum);
312                     kHz = (kHz << 4) | (cpuNum & 0xF);
313 #endif
314                     // save values in FIFO queues for dumpsys
315                     // these stores #1, #2, #3 are not atomic with respect to each other,
316                     // or with respect to store #4 below
317                     dumpState->mMonotonicNs[i] = monotonicNs;
318                     dumpState->mLoadNs[i] = loadNs;
319 #ifdef CPU_FREQUENCY_STATISTICS
320                     dumpState->mCpukHz[i] = kHz;
321 #endif
322                     // this store #4 is not atomic with respect to stores #1, #2, #3 above, but
323                     // the newest open & oldest closed halves are atomic with respect to each other
324                     dumpState->mBounds = bounds;
325                     ATRACE_INT("cycle_ms", monotonicNs / 1000000);
326                     ATRACE_INT("load_us", loadNs / 1000);
327                 }
328 #endif
329             } else {
330                 // first time through the loop
331                 oldTsValid = true;
332                 sleepNs = periodNs;
333                 ignoreNextOverrun = true;
334             }
335             oldTs = newTs;
336         } else {
337             // monotonic clock is broken
338             oldTsValid = false;
339             sleepNs = periodNs;
340         }
341 
342     }   // for (;;)
343 
344     // never return 'true'; Thread::_threadLoop() locks mutex which can result in priority inversion
345 }
346 
347 }   // namespace android
348