From: Dongwoo Lee Date: Thu, 12 Jan 2023 01:01:22 +0000 (+0900) Subject: resource-manager: Add epoll type listener support X-Git-Tag: accepted/tizen/7.0/unified/20230216.140319^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c1f25110222b4a8cdaebc73b018dbd835529fab5;p=platform%2Fcore%2Fsystem%2Flibsyscommon.git resource-manager: Add epoll type listener support To support ease of use for epoll type listener, this introduces following APIs: - syscommon_resman_register_epoll_listener: register fd to epoll listener worker thread and returns listener handle id. - syscommon_resman_unregister_epoll_listener: unregister fd from worker for the correspongding handle id. (fd is not closed by this function, so caller resource driver should be close fd by its own responsibility. Change-Id: Ifc42b30903cd222a35b93f9895ae0cd183a3a504 Signed-off-by: Dongwoo Lee --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 50dabeb..ce20dbf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ SET(libsyscommon_SRCS src/libcommon/common.c src/resource-manager/resource-manager.c src/resource-manager/resource-device.c + src/resource-manager/resource-listener-epoll.c ) SET(HEADERS src/libgdbus/libgdbus.h diff --git a/src/resource-manager/resource-listener-epoll.c b/src/resource-manager/resource-listener-epoll.c new file mode 100644 index 0000000..2806208 --- /dev/null +++ b/src/resource-manager/resource-listener-epoll.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include + +#include "resource-manager.h" +#include "resource-listener.h" + +/* listener common */ +struct listener_handle { + int id; + int resource_id; + const struct syscommon_resman_resource_attribute *attr; + void *action_param; + unsigned long priv; +}; + +static GHashTable *g_listener_hash_table; +static pthread_mutex_t g_listener_lock = PTHREAD_MUTEX_INITIALIZER; +static int g_listener_handle_id; + +/* epoll listener */ + +#define EPOLL_LISTENER_DEFAULT_POLLING_RATE 3 /* seconds */ +#define EPOLL_LISTENER_DEFAULT_MAX_EVENTS 100 + +static int g_epoll_fd = -1; +static pthread_mutex_t g_epoll_lock = PTHREAD_MUTEX_INITIALIZER; +static unsigned int g_epoll_worker_refcount; + +static int add_listener(struct listener_handle *handle) +{ + if (!handle) + return -EINVAL; + + if (!g_listener_hash_table) { + g_listener_hash_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free); + if (!g_listener_hash_table) + return -ENOMEM; + } + + g_hash_table_insert(g_listener_hash_table, GINT_TO_POINTER(handle->id), handle); + + return 0; +} + +static struct listener_handle *create_listener_handle(void) +{ + struct listener_handle *handle; + + handle = malloc(sizeof(struct listener_handle)); + if (!handle) + return NULL; + + pthread_mutex_lock(&g_listener_lock); + handle->id = ++g_listener_handle_id; + pthread_mutex_unlock(&g_listener_lock); + + return handle; +} + +static struct listener_handle *find_listener(int id) +{ + if (!g_listener_hash_table) + return NULL; + + return g_hash_table_lookup(g_listener_hash_table, GINT_TO_POINTER(id)); +} + + +static gboolean handle_epoll_event(gpointer user_data) +{ + struct listener_handle *handle = user_data; + const struct syscommon_resman_resource_attribute *attr; + + if (!handle) + return G_SOURCE_REMOVE; + + attr = handle->attr; + attr->listener_ops.action(handle->resource_id, attr, handle->action_param, SYSCOMMON_RESMAN_LISTENER_TYPE_EPOLL); + + return G_SOURCE_CONTINUE; +} + +static void epoll_worker_get(void) +{ + pthread_mutex_lock(&g_epoll_lock); + g_epoll_worker_refcount++; + pthread_mutex_unlock(&g_epoll_lock); +} + +static void epoll_worker_put(void) +{ + pthread_mutex_lock(&g_epoll_lock); + g_epoll_worker_refcount--; + pthread_mutex_unlock(&g_epoll_lock); +} + +static int epoll_worker(void *arg) +{ + struct epoll_event *event; + + while (1) { + struct epoll_event epoll_events[EPOLL_LISTENER_DEFAULT_MAX_EVENTS]; + int event_count, i; + + event_count = epoll_wait(g_epoll_fd, epoll_events, EPOLL_LISTENER_DEFAULT_MAX_EVENTS, 0); + + pthread_mutex_lock(&g_epoll_lock); + if (g_epoll_worker_refcount == 0) { + /* there is no one using epoll worker */ + pthread_mutex_unlock(&g_epoll_lock); + break; + } + pthread_mutex_unlock(&g_epoll_lock); + + for (i = 0, event = &epoll_events[0]; i < event_count; i++, event++) { + if (event->events & EPOLLERR || event->events & EPOLLHUP) + continue; + + g_idle_add(handle_epoll_event, event->data.ptr); + } + sleep(EPOLL_LISTENER_DEFAULT_POLLING_RATE); + } + + close(g_epoll_fd); + g_epoll_fd = -1; + + return 0; +} + +int +syscommon_resman_register_epoll_listener(int resource_id, + const struct syscommon_resman_resource_attribute *attr, + int fd, void *action_param) +{ + struct listener_handle *handle; + struct epoll_event epoll_event; + int ret; + + epoll_worker_get(); + + if (g_epoll_fd < 0) { + pthread_t th; + + g_epoll_fd = epoll_create(1); + if (g_epoll_fd < 0) { + ret = -errno; + goto err_worker_put; + } + + ret = pthread_create(&th, NULL, (void *)epoll_worker, NULL); + if (ret < 0) { + ret = -errno; + goto err_epoll; + } + + pthread_detach(th); + } + + handle = create_listener_handle(); + if (!handle) { + ret = -ENOMEM; + goto err_epoll; + } + + handle->resource_id = resource_id; + handle->attr = attr; + handle->action_param = action_param; + handle->priv = fd; + + epoll_event.events = EPOLLPRI; + epoll_event.data.ptr = handle; + + ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &epoll_event); + if (ret < 0) { + ret = -errno; + goto err_create; + } + + add_listener(handle); + + return handle->id; + +err_create: + free(handle); +err_epoll: + if (g_epoll_fd > 0) { + close(g_epoll_fd); + g_epoll_fd = -1; + } +err_worker_put: + epoll_worker_put(); + return ret; +} + +void +syscommon_resman_unregister_epoll_listener(int handle_id) +{ + struct listener_handle *handle = find_listener(handle_id); + int fd; + + if (!handle) + return; + + fd = handle->priv; + + g_hash_table_remove(g_listener_hash_table, GINT_TO_POINTER(handle_id)); + epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, fd, NULL); + epoll_worker_put(); +} diff --git a/src/resource-manager/resource-listener.h b/src/resource-manager/resource-listener.h index 980cf00..1394f51 100644 --- a/src/resource-manager/resource-listener.h +++ b/src/resource-manager/resource-listener.h @@ -25,4 +25,9 @@ enum syscommon_resman_listener_type { SYSCOMMON_RESMAN_LISTENER_TYPE_MAX, }; +struct syscommon_resman_resource_attribute; + +int syscommon_resman_register_epoll_listener(int resource_id, const struct syscommon_resman_resource_attribute *attr, int fd, void *param); +void syscommon_resman_unregister_epoll_listener(int handle_id); + #endif