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