universal7904: camera: create a worker thread to handle all callbacks to avoid hard locking the camera hal with single threaded clients
Change-Id: I31e5b4e874d32ffdceb04aaf5cdd4b805a34f26e
Signed-off-by: SamarV-121 <samarvispute121@pm.me>
diff --git a/hardware/camera/Android.mk b/hardware/camera/Android.mk
index 296b57d..b6c8784 100644
--- a/hardware/camera/Android.mk
+++ b/hardware/camera/Android.mk
@@ -4,7 +4,8 @@
LOCAL_SRC_FILES := \
CameraWrapper.cpp \
Camera2Wrapper.cpp \
- Camera3Wrapper.cpp
+ Camera3Wrapper.cpp \
+ CallbackWorkerThread.cpp
LOCAL_STATIC_LIBRARIES := libbase libarect
LOCAL_SHARED_LIBRARIES := \
diff --git a/hardware/camera/CallbackWorkerThread.cpp b/hardware/camera/CallbackWorkerThread.cpp
new file mode 100644
index 0000000..676e6bd
--- /dev/null
+++ b/hardware/camera/CallbackWorkerThread.cpp
@@ -0,0 +1,219 @@
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "Camera2WrapperCbThread"
+
+#include "CallbackWorkerThread.h"
+#include <iostream>
+#include <cutils/log.h>
+
+using namespace std;
+
+#define MSG_EXIT_THREAD 1
+#define MSG_EXECUTE_CALLBACK 2
+#define MSG_UPDATE_CALLBACKS 3
+
+struct ThreadMsg
+{
+ ThreadMsg(int i, const void* m, long long ts) { id = i; msg = m; CallerTS = ts; }
+ int id;
+ const void* msg;
+ long long CallerTS;
+};
+
+CallbackWorkerThread::CallbackWorkerThread() : m_thread(0) {
+}
+
+CallbackWorkerThread::~CallbackWorkerThread() {
+ ExitThread();
+}
+
+bool CallbackWorkerThread::CreateThread() {
+ if (!m_thread)
+ m_thread = new thread(&CallbackWorkerThread::Process, this);
+ return true;
+}
+
+void CallbackWorkerThread::ExitThread() {
+ if (!m_thread)
+ return;
+
+ /* Create the exit thread worker message */
+ ThreadMsg* threadMsg = new ThreadMsg(MSG_EXIT_THREAD, 0, GetTimestamp());
+
+ /* Add it to the message queue */
+ {
+ lock_guard<mutex> lock(m_mutex);
+ m_queue.push(threadMsg);
+ m_cv.notify_one();
+ }
+
+ /* Join the thread and then cleanup */
+ m_thread->join();
+ delete m_thread;
+ m_thread = 0;
+}
+
+void CallbackWorkerThread::AddCallback(const WorkerMessage* data) {
+ /* Assert that the thread exists */
+ assert(!m_thread);
+
+ /* Create a new worker thread message from the data */
+ ThreadMsg* threadMsg = new ThreadMsg(MSG_EXECUTE_CALLBACK, data, GetTimestamp());
+
+ /* Add it to our worker queue and notify the worker */
+ std::unique_lock<std::mutex> lk(m_mutex);
+ m_queue.push(threadMsg);
+ m_cv.notify_one();
+}
+
+void CallbackWorkerThread::SetCallbacks(const CallbackData* data) {
+ /* Assert that the thread exists */
+ assert(!m_thread);
+
+ /* Create a new worker thread message from the callback data */
+ ThreadMsg* threadMsg = new ThreadMsg(MSG_UPDATE_CALLBACKS, data, GetTimestamp());
+
+ /* Add it to our worker queue and notify the worker */
+ std::unique_lock<std::mutex> lk(m_mutex);
+ m_queue.push(threadMsg);
+ m_cv.notify_one();
+}
+
+void CallbackWorkerThread::ClearCallbacks() {
+ /* Assert that the thread exists */
+ assert(!m_thread);
+
+ /* Lock the mutex and clear the message queue */
+ std::unique_lock<std::mutex> lk(m_mutex);
+
+ ALOGV("%s: Clearing %i messages", __FUNCTION__, m_queue.size());
+
+ /* Whilst the queue is not empty */
+ while (!m_queue.empty()) {
+ /* Pop the message from the queue and delete the allocated data */
+ ThreadMsg* msg = m_queue.front();
+ m_queue.pop();
+ delete msg;
+ }
+
+ m_cv.notify_one();
+}
+
+void CallbackWorkerThread::Process() {
+ camera_notify_callback UserNotifyCb = NULL;
+ camera_data_callback UserDataCb = NULL;
+
+ while (1) {
+ ThreadMsg* msg = 0;
+ {
+ /* Wait for a message to be added to the queue */
+ std::unique_lock<std::mutex> lk(m_mutex);
+ while (m_queue.empty())
+ m_cv.wait(lk);
+
+ if (m_queue.empty())
+ continue;
+
+ msg = m_queue.front();
+ m_queue.pop();
+ }
+
+ switch (msg->id) {
+ case MSG_EXECUTE_CALLBACK:
+ {
+ /* Assert that we have a valid message */
+ assert(msg->msg != NULL);
+
+ /* Cast the the ThreadMsg void* data back to a WorkerMessage* */
+ const WorkerMessage* userData = static_cast<const WorkerMessage*>(msg->msg);
+
+ /* If the callback is not stale (newer than 5mS) */
+ if(GetTimestamp() - msg->CallerTS < 5) {
+ /* If the callback type is set to notifycb */
+ if(userData->CbType == CB_TYPE_NOTIFY) {
+ /* Execute the users notify callback if it is valid */
+ if(UserNotifyCb != NULL) {
+ ALOGV("%s: UserNotifyCb: %i %i %i %p", __FUNCTION__, userData->msg_type, userData->ext1, userData->ext2, userData->user);
+ UserNotifyCb(userData->msg_type, userData->ext1, userData->ext2, userData->user);
+ }
+ } /* If the callback type is set to notifycb */
+ else if(userData->CbType == CB_TYPE_DATA) {
+ /* Execute the users data callback if it is valid */
+ if(UserDataCb != NULL) {
+ ALOGV("%s: UserDataCb: %i %p %i %p %p", __FUNCTION__, userData->msg_type, userData->data, userData->index, userData->metadata, userData->user);
+ UserDataCb(userData->msg_type, userData->data, userData->index, userData->metadata, userData->user);
+ }
+ }
+ } else {
+ /* If the callback type is set to notifycb */
+ if(userData->CbType == CB_TYPE_NOTIFY) {
+ ALOGV("%s: UserNotifyCb Stale: %llimS old", __FUNCTION__, GetTimestamp() - msg->CallerTS);
+ } /* If the callback type is set to notifycb */
+ else if(userData->CbType == CB_TYPE_DATA) {
+ ALOGV("%s: UserDataCb Stale: %llimS old", __FUNCTION__, GetTimestamp() - msg->CallerTS);
+ }
+ }
+
+ /* Cleanup allocated data */
+ delete userData;
+ delete msg;
+ break;
+ }
+
+ case MSG_UPDATE_CALLBACKS:
+ {
+ /* Assert that we have a valid message */
+ assert(msg->msg != NULL);
+
+ /* Cast the the ThreadMsg void* data back to a CallbackData* */
+ const CallbackData* callbackData = static_cast<const CallbackData*>(msg->msg);
+
+ ALOGV("%s: UpdateCallbacks", __FUNCTION__);
+
+ /* Copy the new callback pointers */
+ UserNotifyCb = callbackData->NewUserNotifyCb;
+ UserDataCb = callbackData->NewUserDataCb;
+
+ /* Cleanup allocated data */
+ delete callbackData;
+ delete msg;
+ break;
+ }
+
+ case MSG_EXIT_THREAD:
+ {
+ /* Delete current message */
+ delete msg;
+ /* Then delete all pending messages in the queue */
+ std::unique_lock<std::mutex> lk(m_mutex);
+ /* Whilst the queue is not empty */
+ while (!m_queue.empty()) {
+ /* Pop the message from the queue and delete the allocated data */
+ msg = m_queue.front();
+ m_queue.pop();
+ delete msg;
+ }
+
+ ALOGV("%s: Exit Thread", __FUNCTION__);
+ return;
+ }
+
+ default:
+ /* Error if we get here */
+ assert(0);
+ }
+ }
+}
+
+
+/* based on current_timestamp() function from stack overflow:
+ * https://stackoverflow.com/questions/3756323/how-to-get-the-current-time-in-milliseconds-from-c-in-linux/17083824
+ */
+
+long long CallbackWorkerThread::GetTimestamp() {
+ struct timeval te;
+ gettimeofday(&te, NULL); // get current time
+ long long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000; // calculate milliseconds
+ return milliseconds;
+}
+
diff --git a/hardware/camera/CallbackWorkerThread.h b/hardware/camera/CallbackWorkerThread.h
new file mode 100644
index 0000000..a304692
--- /dev/null
+++ b/hardware/camera/CallbackWorkerThread.h
@@ -0,0 +1,77 @@
+#ifndef _THREAD_STD_H
+#define _THREAD_STD_H
+
+#include <thread>
+#include <queue>
+#include <mutex>
+#include <atomic>
+#include <condition_variable>
+
+#include <sys/time.h>
+
+#include <hardware/camera.h>
+#include <hardware/camera2.h>
+
+#define CB_TYPE_NONE 0
+#define CB_TYPE_NOTIFY 1
+#define CB_TYPE_DATA 2
+
+struct WorkerMessage {
+ /* Worker callback type */
+ int32_t CbType;
+
+ /* Callback data */
+ int32_t msg_type;
+ const camera_memory_t *data;
+ unsigned int index;
+ camera_frame_metadata_t *metadata;
+ void *user;
+ int32_t ext1;
+ int32_t ext2;
+};
+
+struct CallbackData {
+ camera_notify_callback NewUserNotifyCb;
+ camera_data_callback NewUserDataCb;
+};
+
+struct ThreadMsg;
+
+class CallbackWorkerThread {
+public:
+ CallbackWorkerThread();
+ ~CallbackWorkerThread();
+
+ /* Creates our worker, returns true on success */
+ bool CreateThread();
+
+ /* Exits the worker thread */
+ void ExitThread();
+
+ /* Sends a new callback to our worker thread */
+ void AddCallback(const WorkerMessage* data);
+
+ /* Sets the callback function pointers for our worker to call */
+ void SetCallbacks(const CallbackData* data);
+
+ /* Clears the worker message queue */
+ void ClearCallbacks(void);
+
+private:
+ CallbackWorkerThread(const CallbackWorkerThread&);
+ CallbackWorkerThread& operator=(const CallbackWorkerThread&);
+
+ long long GetTimestamp();
+
+ /* Entry point for the worker thread */
+ void Process();
+
+ std::thread* m_thread;
+ std::queue<ThreadMsg*> m_queue;
+ std::mutex m_mutex;
+ std::condition_variable m_cv;
+ const char* m_name;
+};
+
+#endif
+
diff --git a/hardware/camera/Camera2Wrapper.cpp b/hardware/camera/Camera2Wrapper.cpp
index 6848720..db3d8da 100644
--- a/hardware/camera/Camera2Wrapper.cpp
+++ b/hardware/camera/Camera2Wrapper.cpp
@@ -21,9 +21,13 @@
#include <cutils/log.h>
#include <unistd.h>
+#include <stdatomic.h>
#include "CameraWrapper.h"
#include "Camera2Wrapper.h"
+#include "CallbackWorkerThread.h"
+
+CallbackWorkerThread cbThread;
#include <sys/time.h>
@@ -138,17 +142,62 @@
return VENDOR_CALL(device, set_preview_window, window);
}
-uint8_t BlockNotifyCb = 0;
-camera_notify_callback UserNotifyCb = NULL;
+atomic_int BlockCbs;
void WrappedNotifyCb (int32_t msg_type, int32_t ext1, int32_t ext2, void *user) {
ALOGV("%s->In", __FUNCTION__);
- /* If the notify callback is valid and we are not blocking callbacks */
- if((UserNotifyCb != NULL) && (BlockNotifyCb == 0)) {
- ALOGV("%s->Calling UserNotifyCb", __FUNCTION__);
- UserNotifyCb(msg_type, ext1, ext2, user);
+ /* Print a log message and return if we currently blocking adding callbacks */
+ if(BlockCbs == 1) {
+ ALOGV("%s->BlockCbs == 1", __FUNCTION__);
+ return;
}
+
+ /* Create message to send to the callback worker */
+ WorkerMessage* newWorkerMessage = new WorkerMessage();
+ newWorkerMessage->CbType = CB_TYPE_NOTIFY;
+
+ /* Copy the callback data to our worker message */
+ newWorkerMessage->msg_type = msg_type;
+ newWorkerMessage->ext1 = ext1;
+ newWorkerMessage->ext2 = ext2;
+ newWorkerMessage->user = user;
+
+ /* Post the message to the callback worker */
+ cbThread.AddCallback(newWorkerMessage);
+
+ /* 5mS delay to slow down the camera hal thread */
+ usleep(5000);
+ ALOGV("%s->Out", __FUNCTION__);
+}
+
+void WrappedDataCb (int32_t msg_type, const camera_memory_t *data, unsigned int index,
+ camera_frame_metadata_t *metadata, void *user) {
+ ALOGV("%s->In, %i, %u", __FUNCTION__, msg_type, index);
+
+ /* Print a log message and return if we currently blocking adding callbacks */
+ if(BlockCbs == 1) {
+ ALOGV("%s->BlockCbs == 1", __FUNCTION__);
+ return;
+ }
+
+ /* Create message to send to the callback worker */
+ WorkerMessage* newWorkerMessage = new WorkerMessage();
+ newWorkerMessage->CbType = CB_TYPE_DATA;
+
+ /* Copy the callback data to our worker message */
+ newWorkerMessage->msg_type = msg_type;
+ newWorkerMessage->data = data;
+ newWorkerMessage->index= index;
+ newWorkerMessage->metadata = metadata;
+ newWorkerMessage->user = user;
+
+ /* Post the message to the callback worker */
+ cbThread.AddCallback(newWorkerMessage);
+
+ /* 20mS delay to slow down the camera hal thread */
+ usleep(20000);
+ ALOGV("%s->Out", __FUNCTION__);
}
static void camera2_set_callbacks(struct camera_device * device,
@@ -163,11 +212,16 @@
if(!device)
return;
- /* Copy the notify_cb to our user pointer */
- UserNotifyCb = notify_cb;
+ /* Create and populate a new callback data structure */
+ CallbackData* newCallbackData = new CallbackData();
+ newCallbackData->NewUserNotifyCb = notify_cb;
+ newCallbackData->NewUserDataCb = data_cb;
+
+ /* Send it to our worker thread */
+ cbThread.SetCallbacks(newCallbackData);
/* Call the set_callbacks function substituting the notify callback with our wrapper */
- VENDOR_CALL(device, set_callbacks, WrappedNotifyCb, data_cb, data_cb_timestamp, get_memory, user);
+ VENDOR_CALL(device, set_callbacks, WrappedNotifyCb, WrappedDataCb, data_cb_timestamp, get_memory, user);
}
static void camera2_enable_msg_type(struct camera_device * device, int32_t msg_type)
@@ -218,7 +272,16 @@
if(!device)
return;
+ /* Block queueing more callbacks */
+ BlockCbs = 1;
+
+ /* Clear the callback queue */
+ cbThread.ClearCallbacks();
+ /* Execute stop_preview */
VENDOR_CALL(device, stop_preview);
+
+ /* Unblock queueing more callbacks */
+ BlockCbs = 0;
}
static int camera2_preview_enabled(struct camera_device * device)
@@ -292,6 +355,9 @@
if(!device)
return -EINVAL;
+ /* Clear the callback queue */
+ cbThread.ClearCallbacks();
+
/* Call the auto_focus function */
Ret = VENDOR_CALL(device, auto_focus);
@@ -309,6 +375,12 @@
if(!device)
return -EINVAL;
+ /* Block queueing more callbacks */
+ BlockCbs = 1;
+
+ /* Clear the callback queue */
+ cbThread.ClearCallbacks();
+
/* Calculate the difference between our guard time and now */
long long TimeDiff = CancelAFTimeGuard - current_timestamp();
/* Post a log message and return success (skipping the call) if the diff is greater than 0 */
@@ -317,14 +389,11 @@
return 0;
}
- /* Block notify callbacks whilst we are in cancel_auto_focus */
- BlockNotifyCb = 1;
-
/* No active time guard so call the vendor function */
Ret = VENDOR_CALL(device, cancel_auto_focus);
- /* Clear the block flag */
- BlockNotifyCb = 0;
+ /* Unblock queueing more callbacks */
+ BlockCbs = 0;
return Ret;
}
@@ -440,7 +509,10 @@
free(wrapper_dev->base.ops);
free(wrapper_dev);
done:
- UserNotifyCb = NULL;
+
+ /* Exit our callback dispatch thread */
+ cbThread.ExitThread();
+
return ret;
}
@@ -465,6 +537,10 @@
android::Mutex::Autolock lock(gCameraWrapperLock);
+ /* Create our callback dispatch thread */
+ cbThread.CreateThread();
+ BlockCbs = 0;
+
ALOGV("%s", __FUNCTION__);
if (name != NULL) {