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