Jelajahi Sumber

component/bt: add new OSI utility "osi_event" to support the scenario that event need only to be handle once.

wangmengyang 3 tahun lalu
induk
melakukan
7c1ee339bc

+ 39 - 0
components/bt/common/osi/include/osi/thread.h

@@ -20,6 +20,7 @@
 #define OSI_THREAD_MAX_TIMEOUT OSI_SEM_MAX_TIMEOUT
 #define OSI_THREAD_MAX_TIMEOUT OSI_SEM_MAX_TIMEOUT
 
 
 struct osi_thread;
 struct osi_thread;
+struct osi_event;
 
 
 typedef struct osi_thread osi_thread_t;
 typedef struct osi_thread osi_thread_t;
 
 
@@ -80,4 +81,42 @@ const char *osi_thread_name(osi_thread_t *thread);
  */
  */
 int osi_thread_queue_wait_size(osi_thread_t *thread, int wq_idx);
 int osi_thread_queue_wait_size(osi_thread_t *thread, int wq_idx);
 
 
+/*
+ * brief: Create an osi_event struct and register the handler function and its argument
+ *        An osi_event is a kind of work that can be posted to the workqueue of osi_thread to process,
+ *        but the work can have at most one instance the thread workqueue before it is processed. This
+ *        allows the "single post, multiple data processing" jobs.
+ * param func: the handler to process the job
+ * param context: the argument to be passed to the handler function when the job is being processed
+ * return: NULL if no memory, otherwise a valid struct pointer
+ */
+struct osi_event *osi_event_create(osi_thread_func_t func, void *context);
+
+/*
+ * brief: Bind an osi_event to a specific work queue for an osi_thread.
+ *        After binding is completed, a function call of API osi_thread_post_event will send a work
+ *        to the workqueue of the thread, with specified queue index.
+ * param func: event: the pointer to osi_event that is created using osi_event_create
+ * param thread: the pointer to osi_thread that is created using osi_thread_create
+ * param queue_idx: the index of the workqueue of the specified osi_thread, with range starting from 0 to work_queue_num - 1
+ * return: true if osi_event binds to the thread's workqueue successfully, otherwise false
+ */
+bool osi_event_bind(struct osi_event* event, osi_thread_t *thread, int queue_idx);
+
+/*
+ * brief: Destroy the osi_event struct created by osi_event_create and free the allocated memory
+ * param event: the pointer to osi_event
+ */
+void osi_event_delete(struct osi_event* event);
+
+/*
+ * brief: try sending a work to the binded thread's workqueue, so that it can be handled by the worker thread
+ * param event: pointer to osi_event, created by osi_event_create
+ * param timeout: post timeout, OSI_THREAD_MAX_TIMEOUT means blocking forever, 0 means never blocking, others means block millisecond
+ * return: true if the message is enqueued to the thread workqueue, otherwise failed
+ * note: if the return value of function is false, it is the case that the workqueue of the thread is full, and users
+ *       are expected to post the event sometime later to get the work handled.
+ */
+bool osi_thread_post_event(struct osi_event *event, uint32_t timeout);
+
 #endif /* __THREAD_H__ */
 #endif /* __THREAD_H__ */

+ 87 - 0
components/bt/common/osi/thread.c

@@ -23,6 +23,7 @@
 #include "freertos/queue.h"
 #include "freertos/queue.h"
 #include "osi/semaphore.h"
 #include "osi/semaphore.h"
 #include "osi/thread.h"
 #include "osi/thread.h"
+#include "osi/mutex.h"
 
 
 struct work_item {
 struct work_item {
     osi_thread_func_t func;
     osi_thread_func_t func;
@@ -50,6 +51,14 @@ struct osi_thread_start_arg {
   int error;
   int error;
 };
 };
 
 
+struct osi_event {
+    struct work_item item;
+    osi_mutex_t lock;
+    uint16_t is_queued;
+    uint16_t queue_idx;
+    osi_thread_t *thread;
+};
+
 static const size_t DEFAULT_WORK_QUEUE_CAPACITY = 100;
 static const size_t DEFAULT_WORK_QUEUE_CAPACITY = 100;
 
 
 static struct work_queue *osi_work_queue_create(size_t capacity)
 static struct work_queue *osi_work_queue_create(size_t capacity)
@@ -364,3 +373,81 @@ int osi_thread_queue_wait_size(osi_thread_t *thread, int wq_idx)
 
 
     return (int)(osi_thead_work_queue_len(thread->work_queues[wq_idx]));
     return (int)(osi_thead_work_queue_len(thread->work_queues[wq_idx]));
 }
 }
+
+
+struct osi_event *osi_event_create(osi_thread_func_t func, void *context)
+{
+    struct osi_event *event = osi_calloc(sizeof(struct osi_event));
+    if (event != NULL) {
+        if (osi_mutex_new(&event->lock) == 0) {
+            event->item.func = func;
+            event->item.context = context;
+            return event;
+        }
+        osi_free(event);
+    }
+
+    return NULL;
+}
+
+void osi_event_delete(struct osi_event* event)
+{
+    if (event != NULL) {
+        osi_mutex_free(&event->lock);
+        memset(event, 0, sizeof(struct osi_event));
+        osi_free(event);
+    }
+}
+
+bool osi_event_bind(struct osi_event* event, osi_thread_t *thread, int queue_idx)
+{
+    if (event == NULL || event->thread != NULL) {
+        return false;
+    }
+
+    if (thread == NULL || queue_idx >= thread->work_queue_num) {
+        return false;
+    }
+
+    event->thread = thread;
+    event->queue_idx = queue_idx;
+
+    return true;
+}
+
+static void osi_thread_generic_event_handler(void *context)
+{
+    struct osi_event *event = (struct osi_event *)context;
+    if (event != NULL && event->item.func != NULL) {
+        osi_mutex_lock(&event->lock, OSI_MUTEX_MAX_TIMEOUT);
+        event->is_queued = 0;
+        osi_mutex_unlock(&event->lock);
+        event->item.func(event->item.context);
+    }
+}
+
+bool osi_thread_post_event(struct osi_event *event, uint32_t timeout)
+{
+    assert(event != NULL && event->thread != NULL);
+    assert(event->queue_idx >= 0 && event->queue_idx < event->thread->work_queue_num);
+    bool ret = false;
+    if (event->is_queued == 0) {
+        uint16_t acquire_cnt = 0;
+        osi_mutex_lock(&event->lock, OSI_MUTEX_MAX_TIMEOUT);
+        event->is_queued += 1;
+        acquire_cnt = event->is_queued;
+        osi_mutex_unlock(&event->lock);
+
+        if (acquire_cnt == 1) {
+            ret = osi_thread_post(event->thread, osi_thread_generic_event_handler, event, event->queue_idx, timeout);
+            if (!ret) {
+                // clear "is_queued" when post failure, to allow for following event posts
+                osi_mutex_lock(&event->lock, OSI_MUTEX_MAX_TIMEOUT);
+                event->is_queued = 0;
+                osi_mutex_unlock(&event->lock);
+            }
+        }
+    }
+
+    return ret;
+}