390 lines
9.3 KiB
C
390 lines
9.3 KiB
C
/****************************************************************************
|
|
* Copyright (C) 2022 Xiaomi Corporation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
***************************************************************************/
|
|
#ifndef __NuttX__
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
|
|
#define LOG_TAG "thread_loop"
|
|
|
|
#include <assert.h>
|
|
#include <pthread.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <unistd.h>
|
|
|
|
#include "bt_config.h"
|
|
#include "bt_list.h"
|
|
#include "uv_thread_loop.h"
|
|
|
|
typedef struct thread_loop {
|
|
char name[64];
|
|
uv_async_t async;
|
|
uv_thread_t thread;
|
|
uv_mutex_t msg_lock;
|
|
uv_sem_t ready;
|
|
uv_sem_t exited;
|
|
uint8_t is_running;
|
|
struct list_node msg_queue;
|
|
} loop_priv_t;
|
|
|
|
typedef struct {
|
|
struct list_node node;
|
|
union {
|
|
thread_func_t func;
|
|
};
|
|
void* msg;
|
|
} internel_msg_t;
|
|
|
|
typedef struct {
|
|
thread_func_t func;
|
|
void* data;
|
|
uv_sem_t signal;
|
|
} signal_msg_t;
|
|
|
|
typedef struct {
|
|
thread_loop_work_t work;
|
|
uv_sem_t signal;
|
|
} signal_work_t;
|
|
|
|
#if defined(ANDROID)
|
|
#define LOOP_THREAD_STACK_SIZE 40960
|
|
#else
|
|
#define LOOP_THREAD_STACK_SIZE 4096
|
|
#endif
|
|
|
|
static void set_ready(void* data)
|
|
{
|
|
loop_priv_t* priv = data;
|
|
|
|
priv->is_running = 1;
|
|
uv_sem_init(&priv->exited, 0);
|
|
uv_sem_post(&priv->ready);
|
|
syslog(LOG_DEBUG, "set_ready");
|
|
}
|
|
|
|
static void set_stop(void* data)
|
|
{
|
|
uv_loop_t* loop = data;
|
|
loop_priv_t* priv = loop->data;
|
|
|
|
priv->is_running = 0;
|
|
uv_close((uv_handle_t*)&priv->async, NULL);
|
|
uv_stop(loop);
|
|
syslog(LOG_DEBUG, "set_stopped");
|
|
}
|
|
|
|
static void thread_sync_callback(void* data)
|
|
{
|
|
signal_msg_t* msg = (signal_msg_t*)data;
|
|
|
|
msg->func(msg->data);
|
|
uv_sem_post(&msg->signal);
|
|
}
|
|
|
|
static void thread_message_callback(uv_async_t* handle)
|
|
{
|
|
internel_msg_t* imsg;
|
|
loop_priv_t* priv = handle->data;
|
|
|
|
for (;;) {
|
|
uv_mutex_lock(&priv->msg_lock);
|
|
imsg = (internel_msg_t*)list_remove_head(&priv->msg_queue);
|
|
uv_mutex_unlock(&priv->msg_lock);
|
|
if (!imsg)
|
|
return;
|
|
|
|
imsg->func(imsg->msg);
|
|
free(imsg);
|
|
}
|
|
}
|
|
|
|
static void thread_schedule_loop(void* data)
|
|
{
|
|
uv_loop_t* loop = data;
|
|
loop_priv_t* priv = loop->data;
|
|
|
|
int ret = uv_async_init(loop, &priv->async, thread_message_callback);
|
|
if (ret != 0) {
|
|
syslog(LOG_ERR, "%s async error: %d", __func__, ret);
|
|
return;
|
|
}
|
|
|
|
priv->async.data = priv;
|
|
syslog(LOG_DEBUG, "%s:%p, async:%p", __func__, loop, &priv->async);
|
|
do_in_thread_loop(loop, set_ready, priv);
|
|
uv_run(loop, UV_RUN_DEFAULT);
|
|
priv->is_running = 0;
|
|
(void)uv_loop_close(loop);
|
|
|
|
syslog(LOG_DEBUG, "%s %s quit", priv->name, __func__);
|
|
|
|
uv_sem_post(&priv->exited);
|
|
}
|
|
|
|
static void handle_close_cb(uv_handle_t* handle)
|
|
{
|
|
free(handle);
|
|
}
|
|
|
|
int thread_loop_init(uv_loop_t* loop)
|
|
{
|
|
int ret;
|
|
loop_priv_t* priv = malloc(sizeof(loop_priv_t));
|
|
if (!priv)
|
|
return -ENOMEM;
|
|
|
|
priv->is_running = 0;
|
|
ret = uv_mutex_init(&priv->msg_lock);
|
|
if (ret != 0) {
|
|
free(priv);
|
|
syslog(LOG_ERR, "%s mutex error: %d", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
list_initialize(&priv->msg_queue);
|
|
ret = uv_loop_init(loop);
|
|
if (ret != 0) {
|
|
list_delete(&priv->msg_queue);
|
|
uv_mutex_destroy(&priv->msg_lock);
|
|
free(priv);
|
|
return ret;
|
|
}
|
|
|
|
loop->data = priv;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int thread_loop_run(uv_loop_t* loop, bool start_thread, const char* name)
|
|
{
|
|
loop_priv_t* priv = loop->data;
|
|
|
|
if (start_thread) {
|
|
int ret = uv_sem_init(&priv->ready, 0);
|
|
if (ret != 0) {
|
|
syslog(LOG_ERR, "%s sem init error: %d", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
uv_thread_options_t options = {
|
|
UV_THREAD_HAS_STACK_SIZE | UV_THREAD_HAS_PRIORITY,
|
|
LOOP_THREAD_STACK_SIZE,
|
|
CONFIG_BLUETOOTH_SERVICE_LOOP_THREAD_PRIORITY + 1
|
|
};
|
|
ret = uv_thread_create_ex(&priv->thread, &options, thread_schedule_loop, (void*)loop);
|
|
if (ret != 0) {
|
|
syslog(LOG_ERR, "loop thread create :%d", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (name != NULL && strlen(name) > 0)
|
|
snprintf(priv->name, sizeof(priv->name), "%s_%d", name, getpid());
|
|
else
|
|
snprintf(priv->name, sizeof(priv->name), "loop_%d", getpid());
|
|
pthread_setname_np(priv->thread, priv->name);
|
|
uv_sem_wait(&priv->ready);
|
|
uv_sem_destroy(&priv->ready);
|
|
syslog(LOG_DEBUG, "%s loop running now !!!", priv->name);
|
|
} else {
|
|
syslog(LOG_DEBUG, "%s loop running now !!!", name);
|
|
thread_schedule_loop(NULL);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void thread_loop_exit(uv_loop_t* loop)
|
|
{
|
|
struct list_node* node;
|
|
struct list_node* tmp;
|
|
|
|
if (!loop || !loop->data)
|
|
return;
|
|
|
|
loop_priv_t* priv = loop->data;
|
|
|
|
if (priv->is_running) {
|
|
do_in_thread_loop(loop, set_stop, (void*)loop);
|
|
uv_sem_wait(&priv->exited);
|
|
uv_sem_destroy(&priv->exited);
|
|
} else {
|
|
if (!uv_loop_is_close(loop)) {
|
|
uv_run(loop, UV_RUN_ONCE);
|
|
(void)uv_loop_close(loop);
|
|
}
|
|
}
|
|
|
|
uv_mutex_lock(&priv->msg_lock);
|
|
list_for_every_safe(&priv->msg_queue, node, tmp)
|
|
{
|
|
list_delete(node);
|
|
free(node);
|
|
}
|
|
list_delete(&priv->msg_queue);
|
|
uv_mutex_unlock(&priv->msg_lock);
|
|
uv_mutex_destroy(&priv->msg_lock);
|
|
free(priv);
|
|
}
|
|
|
|
uv_poll_t* thread_loop_poll_fd(uv_loop_t* loop, int fd, int pevents, uv_poll_cb cb, void* userdata)
|
|
{
|
|
assert(fd);
|
|
assert(cb);
|
|
|
|
uv_poll_t* handle = (uv_poll_t*)malloc(sizeof(uv_poll_t));
|
|
if (!handle)
|
|
return NULL;
|
|
|
|
handle->data = userdata;
|
|
int ret = uv_poll_init(loop, handle, fd);
|
|
if (ret != 0)
|
|
goto error;
|
|
|
|
ret = uv_poll_start(handle, pevents, cb);
|
|
if (ret != 0)
|
|
goto error;
|
|
|
|
return handle;
|
|
|
|
error:
|
|
syslog(LOG_ERR, "%s failed: %d", __func__, ret);
|
|
free(handle);
|
|
return NULL;
|
|
}
|
|
|
|
int thread_loop_reset_poll(uv_poll_t* poll, int pevents, uv_poll_cb cb)
|
|
{
|
|
assert(poll);
|
|
|
|
uv_poll_stop(poll);
|
|
|
|
return uv_poll_start(poll, pevents, cb);
|
|
}
|
|
|
|
void thread_loop_remove_poll(uv_poll_t* poll)
|
|
{
|
|
if (!poll)
|
|
return;
|
|
|
|
uv_poll_stop(poll);
|
|
uv_close((uv_handle_t*)poll, handle_close_cb);
|
|
}
|
|
|
|
uv_timer_t* thread_loop_timer(uv_loop_t* loop, uint64_t timeout, uint64_t repeat, uv_timer_cb cb, void* userdata)
|
|
{
|
|
if (!cb)
|
|
return NULL;
|
|
|
|
uv_timer_t* handle = malloc(sizeof(uv_timer_t));
|
|
if (!handle)
|
|
return NULL;
|
|
|
|
uv_timer_init(loop, handle);
|
|
handle->data = userdata;
|
|
uv_timer_start(handle, cb, timeout, repeat);
|
|
|
|
return handle;
|
|
}
|
|
|
|
uv_timer_t* thread_loop_timer_no_repeating(uv_loop_t* loop, uint64_t timeout, uv_timer_cb cb, void* userdata)
|
|
{
|
|
return thread_loop_timer(loop, timeout, 0, cb, userdata);
|
|
}
|
|
|
|
void thread_loop_cancel_timer(uv_timer_t* timer)
|
|
{
|
|
if (!timer)
|
|
return;
|
|
|
|
uv_timer_stop(timer);
|
|
uv_close((uv_handle_t*)timer, handle_close_cb);
|
|
}
|
|
|
|
void do_in_thread_loop(uv_loop_t* loop, thread_func_t func, void* data)
|
|
{
|
|
loop_priv_t* priv = loop->data;
|
|
internel_msg_t* msg = (internel_msg_t*)malloc(sizeof(internel_msg_t));
|
|
assert(msg);
|
|
|
|
msg->func = func;
|
|
msg->msg = data;
|
|
|
|
uv_mutex_lock(&priv->msg_lock);
|
|
list_add_tail(&priv->msg_queue, &msg->node);
|
|
uv_mutex_unlock(&priv->msg_lock);
|
|
|
|
uv_async_send(&priv->async);
|
|
}
|
|
|
|
void do_in_thread_loop_sync(uv_loop_t* loop, thread_func_t func, void* data)
|
|
{
|
|
signal_msg_t msg;
|
|
|
|
msg.func = func;
|
|
msg.data = data;
|
|
uv_sem_init(&msg.signal, 0);
|
|
do_in_thread_loop(loop, thread_sync_callback, &msg);
|
|
uv_sem_wait(&msg.signal);
|
|
uv_sem_destroy(&msg.signal);
|
|
}
|
|
|
|
static void work_sync_cb(uv_work_t* req)
|
|
{
|
|
signal_work_t* work = req->data;
|
|
assert(work);
|
|
|
|
if (work->work.work_cb)
|
|
work->work.work_cb(&work->work, work->work.userdata);
|
|
}
|
|
|
|
static void after_work_sync_cb(uv_work_t* req, int status)
|
|
{
|
|
signal_work_t* work = req->data;
|
|
assert(status == 0);
|
|
assert(work);
|
|
|
|
if (work->work.after_work_cb)
|
|
work->work.after_work_cb(&work->work, work->work.userdata);
|
|
|
|
uv_sem_post(&work->signal);
|
|
}
|
|
|
|
void thread_loop_work_sync(uv_loop_t* loop, void* user_data, thread_work_cb_t work_cb,
|
|
thread_after_work_cb_t after_work_cb)
|
|
{
|
|
signal_work_t* work = (signal_work_t*)calloc(1, sizeof(signal_work_t));
|
|
if (work == NULL)
|
|
return;
|
|
|
|
work->work.userdata = user_data;
|
|
work->work.work_cb = work_cb;
|
|
work->work.after_work_cb = after_work_cb;
|
|
work->work.work.data = work;
|
|
uv_sem_init(&work->signal, 0);
|
|
|
|
if (uv_queue_work(loop, &work->work.work, work_sync_cb, after_work_sync_cb) != 0) {
|
|
syslog(LOG_DEBUG, "%s uv_queue_work failed", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
uv_sem_wait(&work->signal);
|
|
exit:
|
|
uv_sem_destroy(&work->signal);
|
|
free(work);
|
|
}
|