resource-manager: Add epoll type listener support 79/287479/6 accepted/tizen/unified/20230221.031433
authorDongwoo Lee <dwoo08.lee@samsung.com>
Thu, 12 Jan 2023 01:01:22 +0000 (10:01 +0900)
committerDongwoo Lee <dwoo08.lee@samsung.com>
Tue, 14 Feb 2023 02:48:54 +0000 (11:48 +0900)
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: Ide38a3816742939c9101e5a92944f8dc2480ff8f
Signed-off-by: Dongwoo Lee <dwoo08.lee@samsung.com>
CMakeLists.txt
src/resource-manager/resource-listener-epoll.c [new file with mode: 0644]
src/resource-manager/resource-listener.h

index 50dabeb..ce20dbf 100644 (file)
@@ -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 (file)
index 0000000..2806208
--- /dev/null
@@ -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 <unistd.h>
+#include <glib.h>
+#include <sys/epoll.h>
+#include <pthread.h>
+
+#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();
+}
index 980cf00..1394f51 100644 (file)
@@ -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