universal7580: un-commonise the audio effects configuration
[GitHub/LineageOS/android_device_samsung_universal7580-common.git] / camera / CallbackWorkerThread.cpp
CommitLineData
9df09228
DW
1/*
2 * Copyright (C) 2019, The LineageOS 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/*
18 * Camera HAL "CallbackWorkerThread" workaround description
19 *
20 * The camera HAL of the Exynos7580 (A3, A5, ... 2016) reguralry deadlocks when using
21 * most camera apps, this happens a lot when using Google Camera but does occur
22 * occasionally in Snap.
23 *
24 * The issue was tracked down to the way that Samsung operates their HAL, all operations
25 * are run in multiple threads and the different callbacks are executed from one of these
26 * threads.
27 *
28 * The deadlocks occur when the camera client executes a function like cancel_auto_focus
29 * or stop_preview and a callback from the HAL occurs at around the same time. The HAL
30 * waits for the callback functions to return before they finish processing the client
31 * calls and the client stops processing callbacks until their calling function completes.
32 *
33 * You end up in a state when both the HAL and the client are waiting for the other
34 * process to finish and so end up with a deadlock of the HAL/Client which only a reboot
35 * can cure.
36 *
37 * This worker thread offloads the actual callbacks to another thread which allows the
38 * HAL to finish the client calls and avoid the deadlock scenario.
39 *
40 */
443860a7
DW
41
42#define LOG_NDEBUG 0
43#define LOG_TAG "Camera2WrapperCbThread"
44
45#include "CallbackWorkerThread.h"
46#include <iostream>
47#include <cutils/log.h>
48
49using namespace std;
50
51#define MSG_EXIT_THREAD 1
52#define MSG_EXECUTE_CALLBACK 2
53#define MSG_UPDATE_CALLBACKS 3
54
55struct ThreadMsg
56{
57 ThreadMsg(int i, const void* m, long long ts) { id = i; msg = m; CallerTS = ts; }
58 int id;
59 const void* msg;
60 long long CallerTS;
61};
62
63CallbackWorkerThread::CallbackWorkerThread() : m_thread(0) {
64}
65
66CallbackWorkerThread::~CallbackWorkerThread() {
67 ExitThread();
68}
69
70bool CallbackWorkerThread::CreateThread() {
71 if (!m_thread)
72 m_thread = new thread(&CallbackWorkerThread::Process, this);
73 return true;
74}
75
76void CallbackWorkerThread::ExitThread() {
77 if (!m_thread)
78 return;
79
80 /* Create the exit thread worker message */
81 ThreadMsg* threadMsg = new ThreadMsg(MSG_EXIT_THREAD, 0, GetTimestamp());
82
83 /* Add it to the message queue */
84 {
85 lock_guard<mutex> lock(m_mutex);
86 m_queue.push(threadMsg);
87 m_cv.notify_one();
88 }
89
90 /* Join the thread and then cleanup */
91 m_thread->join();
92 delete m_thread;
93 m_thread = 0;
94}
95
96void CallbackWorkerThread::AddCallback(const WorkerMessage* data) {
97 /* Assert that the thread exists */
9df09228 98 ALOG_ASSERT(m_thread != NULL);
443860a7
DW
99
100 /* Create a new worker thread message from the data */
101 ThreadMsg* threadMsg = new ThreadMsg(MSG_EXECUTE_CALLBACK, data, GetTimestamp());
102
103 /* Add it to our worker queue and notify the worker */
104 std::unique_lock<std::mutex> lk(m_mutex);
105 m_queue.push(threadMsg);
106 m_cv.notify_one();
107}
108
109void CallbackWorkerThread::SetCallbacks(const CallbackData* data) {
110 /* Assert that the thread exists */
9df09228 111 ALOG_ASSERT(m_thread != NULL);
443860a7
DW
112
113 /* Create a new worker thread message from the callback data */
114 ThreadMsg* threadMsg = new ThreadMsg(MSG_UPDATE_CALLBACKS, data, GetTimestamp());
115
116 /* Add it to our worker queue and notify the worker */
117 std::unique_lock<std::mutex> lk(m_mutex);
118 m_queue.push(threadMsg);
119 m_cv.notify_one();
120}
121
122void CallbackWorkerThread::ClearCallbacks() {
123 /* Assert that the thread exists */
9df09228 124 ALOG_ASSERT(m_thread != NULL);
443860a7
DW
125
126 /* Lock the mutex and clear the message queue */
127 std::unique_lock<std::mutex> lk(m_mutex);
128
129 ALOGV("%s: Clearing %i messages", __FUNCTION__, m_queue.size());
130
131 /* Whilst the queue is not empty */
132 while (!m_queue.empty()) {
133 /* Pop the message from the queue and delete the allocated data */
134 ThreadMsg* msg = m_queue.front();
135 m_queue.pop();
136 delete msg;
137 }
138
139 m_cv.notify_one();
140}
141
142void CallbackWorkerThread::Process() {
143 camera_notify_callback UserNotifyCb = NULL;
144 camera_data_callback UserDataCb = NULL;
145
146 while (1) {
147 ThreadMsg* msg = 0;
148 {
149 /* Wait for a message to be added to the queue */
150 std::unique_lock<std::mutex> lk(m_mutex);
151 while (m_queue.empty())
152 m_cv.wait(lk);
153
154 if (m_queue.empty())
155 continue;
156
157 msg = m_queue.front();
158 m_queue.pop();
159 }
160
161 switch (msg->id) {
162 case MSG_EXECUTE_CALLBACK:
163 {
164 /* Assert that we have a valid message */
9df09228 165 ALOG_ASSERT(msg->msg != NULL);
443860a7
DW
166
167 /* Cast the the ThreadMsg void* data back to a WorkerMessage* */
168 const WorkerMessage* userData = static_cast<const WorkerMessage*>(msg->msg);
169
170 /* If the callback is not stale (newer than 5mS) */
171 if(GetTimestamp() - msg->CallerTS < 5) {
172 /* If the callback type is set to notifycb */
173 if(userData->CbType == CB_TYPE_NOTIFY) {
174 /* Execute the users notify callback if it is valid */
175 if(UserNotifyCb != NULL) {
176 ALOGV("%s: UserNotifyCb: %i %i %i %p", __FUNCTION__, userData->msg_type, userData->ext1, userData->ext2, userData->user);
177 UserNotifyCb(userData->msg_type, userData->ext1, userData->ext2, userData->user);
178 }
179 } /* If the callback type is set to notifycb */
180 else if(userData->CbType == CB_TYPE_DATA) {
181 /* Execute the users data callback if it is valid */
182 if(UserDataCb != NULL) {
183 ALOGV("%s: UserDataCb: %i %p %i %p %p", __FUNCTION__, userData->msg_type, userData->data, userData->index, userData->metadata, userData->user);
184 UserDataCb(userData->msg_type, userData->data, userData->index, userData->metadata, userData->user);
185 }
186 }
187 } else {
188 /* If the callback type is set to notifycb */
189 if(userData->CbType == CB_TYPE_NOTIFY) {
190 ALOGV("%s: UserNotifyCb Stale: %llimS old", __FUNCTION__, GetTimestamp() - msg->CallerTS);
191 } /* If the callback type is set to notifycb */
192 else if(userData->CbType == CB_TYPE_DATA) {
193 ALOGV("%s: UserDataCb Stale: %llimS old", __FUNCTION__, GetTimestamp() - msg->CallerTS);
194 }
195 }
196
197 /* Cleanup allocated data */
198 delete userData;
199 delete msg;
200 break;
201 }
202
203 case MSG_UPDATE_CALLBACKS:
204 {
205 /* Assert that we have a valid message */
9df09228 206 ALOG_ASSERT(msg->msg != NULL);
443860a7
DW
207
208 /* Cast the the ThreadMsg void* data back to a CallbackData* */
209 const CallbackData* callbackData = static_cast<const CallbackData*>(msg->msg);
210
211 ALOGV("%s: UpdateCallbacks", __FUNCTION__);
212
213 /* Copy the new callback pointers */
214 UserNotifyCb = callbackData->NewUserNotifyCb;
215 UserDataCb = callbackData->NewUserDataCb;
216
217 /* Cleanup allocated data */
218 delete callbackData;
219 delete msg;
220 break;
221 }
222
223 case MSG_EXIT_THREAD:
224 {
225 /* Delete current message */
226 delete msg;
227 /* Then delete all pending messages in the queue */
228 std::unique_lock<std::mutex> lk(m_mutex);
229 /* Whilst the queue is not empty */
230 while (!m_queue.empty()) {
231 /* Pop the message from the queue and delete the allocated data */
232 msg = m_queue.front();
233 m_queue.pop();
234 delete msg;
235 }
236
237 ALOGV("%s: Exit Thread", __FUNCTION__);
238 return;
239 }
240
241 default:
242 /* Error if we get here */
9df09228 243 ALOG_ASSERT(0);
443860a7
DW
244 }
245 }
246}
247
248
249/* based on current_timestamp() function from stack overflow:
250 * https://stackoverflow.com/questions/3756323/how-to-get-the-current-time-in-milliseconds-from-c-in-linux/17083824
251 */
252
253long long CallbackWorkerThread::GetTimestamp() {
254 struct timeval te;
255 gettimeofday(&te, NULL); // get current time
256 long long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000; // calculate milliseconds
257 return milliseconds;
258}
259