blob: dda0e4a6fdcb12e4ba07c562023fdb06a9b1333b [file] [log] [blame]
Mikhail Naganov0b9c5fe2022-08-08 18:28:36 +00001/*
2 * Copyright (C) 2022 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#include <pthread.h>
18#include <sched.h>
19#include <sys/resource.h>
20
21#include "include/StreamWorker.h"
22
23namespace android::hardware::audio::common::internal {
24
25bool ThreadController::start(const std::string& name, int priority) {
26 mThreadName = name;
27 mThreadPriority = priority;
28 mWorker = std::thread(&ThreadController::workerThread, this);
29 std::unique_lock<std::mutex> lock(mWorkerLock);
30 android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
31 mWorkerCv.wait(lock, [&]() {
32 android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
33 return mWorkerState == WorkerState::RUNNING || !mError.empty();
34 });
35 mWorkerStateChangeRequest = false;
36 return mWorkerState == WorkerState::RUNNING;
37}
38
39void ThreadController::stop() {
40 {
41 std::lock_guard<std::mutex> lock(mWorkerLock);
42 if (mWorkerState != WorkerState::STOPPED) {
43 mWorkerState = WorkerState::STOPPED;
44 mWorkerStateChangeRequest = true;
45 }
46 }
Mikhail Naganov70529732022-10-20 01:16:34 +000047 join();
48}
49
50void ThreadController::join() {
Mikhail Naganov0b9c5fe2022-08-08 18:28:36 +000051 if (mWorker.joinable()) {
52 mWorker.join();
53 }
54}
55
56bool ThreadController::waitForAtLeastOneCycle() {
57 WorkerState newState;
58 switchWorkerStateSync(WorkerState::RUNNING, WorkerState::PAUSE_REQUESTED, &newState);
59 if (newState != WorkerState::PAUSED) return false;
60 switchWorkerStateSync(newState, WorkerState::RESUME_REQUESTED, &newState);
61 return newState == WorkerState::RUNNING;
62}
63
64void ThreadController::switchWorkerStateSync(WorkerState oldState, WorkerState newState,
65 WorkerState* finalState) {
66 std::unique_lock<std::mutex> lock(mWorkerLock);
67 android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
68 if (mWorkerState != oldState) {
69 if (finalState) *finalState = mWorkerState;
70 return;
71 }
72 mWorkerState = newState;
73 mWorkerStateChangeRequest = true;
74 mWorkerCv.wait(lock, [&]() {
75 android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
76 return mWorkerState != newState;
77 });
78 if (finalState) *finalState = mWorkerState;
79}
80
81void ThreadController::workerThread() {
82 using Status = StreamLogic::Status;
83
84 std::string error = mLogic->init();
85 if (error.empty() && !mThreadName.empty()) {
86 std::string compliantName(mThreadName.substr(0, 15));
87 if (int errCode = pthread_setname_np(pthread_self(), compliantName.c_str()); errCode != 0) {
88 error.append("Failed to set thread name: ").append(strerror(errCode));
89 }
90 }
91 if (error.empty() && mThreadPriority != ANDROID_PRIORITY_DEFAULT) {
92 if (int result = setpriority(PRIO_PROCESS, 0, mThreadPriority); result != 0) {
93 int errCode = errno;
94 error.append("Failed to set thread priority: ").append(strerror(errCode));
95 }
96 }
97 {
98 std::lock_guard<std::mutex> lock(mWorkerLock);
99 mWorkerState = error.empty() ? WorkerState::RUNNING : WorkerState::STOPPED;
100 mError = error;
101 }
102 mWorkerCv.notify_one();
103 if (!error.empty()) return;
104
105 for (WorkerState state = WorkerState::RUNNING; state != WorkerState::STOPPED;) {
106 bool needToNotify = false;
107 if (Status status = state != WorkerState::PAUSED ? mLogic->cycle()
108 : (sched_yield(), Status::CONTINUE);
109 status == Status::CONTINUE) {
110 {
111 // See https://developer.android.com/training/articles/smp#nonracing
112 android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
113 if (!mWorkerStateChangeRequest.load(std::memory_order_relaxed)) continue;
114 }
115 //
116 // Pause and resume are synchronous. One worker cycle must complete
117 // before the worker indicates a state change. This is how 'mWorkerState' and
118 // 'state' interact:
119 //
120 // mWorkerState == RUNNING
121 // client sets mWorkerState := PAUSE_REQUESTED
122 // last workerCycle gets executed, state := mWorkerState := PAUSED by us
123 // (or the workers enters the 'error' state if workerCycle fails)
124 // client gets notified about state change in any case
125 // thread is doing a busy wait while 'state == PAUSED'
126 // client sets mWorkerState := RESUME_REQUESTED
127 // state := mWorkerState (RESUME_REQUESTED)
128 // mWorkerState := RUNNING, but we don't notify the client yet
129 // first workerCycle gets executed, the code below triggers a client notification
130 // (or if workerCycle fails, worker enters 'error' state and also notifies)
131 // state := mWorkerState (RUNNING)
132 std::lock_guard<std::mutex> lock(mWorkerLock);
133 if (state == WorkerState::RESUME_REQUESTED) {
134 needToNotify = true;
135 }
136 state = mWorkerState;
137 if (mWorkerState == WorkerState::PAUSE_REQUESTED) {
138 state = mWorkerState = WorkerState::PAUSED;
139 needToNotify = true;
140 } else if (mWorkerState == WorkerState::RESUME_REQUESTED) {
141 mWorkerState = WorkerState::RUNNING;
142 }
143 } else {
144 std::lock_guard<std::mutex> lock(mWorkerLock);
145 if (state == WorkerState::RESUME_REQUESTED ||
146 mWorkerState == WorkerState::PAUSE_REQUESTED) {
147 needToNotify = true;
148 }
149 state = mWorkerState = WorkerState::STOPPED;
150 if (status == Status::ABORT) {
151 mError = "Received ABORT from the logic cycle";
152 }
153 }
154 if (needToNotify) {
155 {
156 std::lock_guard<std::mutex> lock(mWorkerLock);
157 mWorkerStateChangeRequest = false;
158 }
159 mWorkerCv.notify_one();
160 }
161 }
162}
163
164} // namespace android::hardware::audio::common::internal