sensord: ipc: add event_loop class 01/123101/3
authorkibak.yoon <kibak.yoon@samsung.com>
Tue, 4 Apr 2017 11:13:14 +0000 (20:13 +0900)
committerkibak.yoon <kibak.yoon@samsung.com>
Wed, 5 Apr 2017 00:52:50 +0000 (09:52 +0900)
- event_loop class is a wrapper of g_main_loop.
- event_loop class has the role of I/O multiplexing.
- event_handler interface can be added to event_loop.

- it would be better to make event_loop interface to use the other main_loop..
  but now, it is not necessary yet.

- todo-list
  - add/remove idle handler

Change-Id: I74dd0636d43319e64fa2701f9b3c662e3f31c575
Signed-off-by: kibak.yoon <kibak.yoon@samsung.com>
src/shared/event_handler.h [new file with mode: 0644]
src/shared/event_loop.cpp [new file with mode: 0644]
src/shared/event_loop.h [new file with mode: 0644]

diff --git a/src/shared/event_handler.h b/src/shared/event_handler.h
new file mode 100644 (file)
index 0000000..26ab899
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * sensord
+ *
+ * Copyright (c) 2017 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.
+ *
+ */
+
+#ifndef __EVENT_HANDLER_H__
+#define __EVENT_HANDLER_H__
+
+namespace ipc {
+
+typedef unsigned int event_condition;
+
+class event_handler {
+public:
+       virtual ~event_handler() {}
+
+       virtual bool handle(int fd, event_condition condition) = 0;
+};
+
+}
+
+#endif /* __EVENT_HANDLER_H__ */
diff --git a/src/shared/event_loop.cpp b/src/shared/event_loop.cpp
new file mode 100644 (file)
index 0000000..9bd596a
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * sensord
+ *
+ * Copyright (c) 2017 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 "event_loop.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/eventfd.h>
+#include <glib.h>
+
+#include "sensor_log.h"
+#include "event_handler.h"
+
+#define BAD_HANDLE 0
+
+using namespace ipc;
+
+static gboolean g_io_handler(GIOChannel *ch, GIOCondition condition, gpointer data)
+{
+       uint64_t id;
+       int fd;
+       bool term;
+       unsigned int cond;
+
+       cond = (unsigned int)condition;
+
+       if (cond & (G_IO_HUP))
+               cond &= ~(G_IO_IN & G_IO_OUT);
+
+       handler_info *info = (handler_info *)data;
+       id = info->id;
+       fd = info->fd;
+       term = info->loop->is_terminator(fd);
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       bool ret = info->handler->handle(fd, (event_condition)cond);
+
+       if (!ret && !term) {
+               info->loop->remove_event(id);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gint on_timer(gpointer data)
+{
+       event_loop *loop = (event_loop *)data;
+       loop->stop();
+
+       return FALSE;
+}
+
+event_loop::event_loop()
+: m_mainloop(NULL)
+, m_running(false)
+, m_terminating(false)
+, m_sequence(1)
+, m_term_fd(-1)
+{
+       m_mainloop = g_main_loop_new(NULL, FALSE);
+}
+
+event_loop::event_loop(GMainLoop *mainloop)
+: m_mainloop(NULL)
+, m_running(true)
+, m_terminating(false)
+, m_sequence(1)
+, m_term_fd(-1)
+{
+       if (m_mainloop) {
+               g_main_loop_quit(m_mainloop);
+               g_main_loop_unref(m_mainloop);
+       }
+
+       m_mainloop = mainloop;
+}
+
+event_loop::~event_loop()
+{
+       remove_all_events();
+}
+
+uint64_t event_loop::add_event(const int fd, const event_condition cond, event_handler *handler)
+{
+       GIOChannel *ch = NULL;
+       GSource *src = NULL;
+
+       retvm_if(m_terminating.load(), BAD_HANDLE,
+                       "Failed to add event, because event_loop is being terminated");
+
+       ch = g_io_channel_unix_new(fd);
+       retvm_if(!ch, BAD_HANDLE, "Failed to create g_io_channel_unix_new");
+
+       src = g_io_create_watch(ch, (GIOCondition)(cond));
+       if (!src) {
+               g_io_channel_unref(ch);
+               _E("Failed to create g_io_create_watch");
+               return BAD_HANDLE;
+       }
+
+       uint64_t id = m_sequence++;
+
+       handler_info *info = new(std::nothrow) handler_info(id, fd, ch, src, handler, this);
+       retvm_if(!info, BAD_HANDLE, "Failed to allocate memory");
+
+       g_source_set_callback(src, (GSourceFunc) g_io_handler, info, NULL);
+       g_source_attach(src, g_main_loop_get_context(m_mainloop));
+
+       m_infos[id] = info;
+
+       /* _D("Added[%llu](fd:%d)", id, fd); */
+       return id;
+}
+
+uint64_t event_loop::add_idle_event(unsigned int priority, idle_handler *handler)
+{
+       GSource *src;
+
+       retvm_if(m_terminating.load(), false,
+                       "Failed to remove event, because event_loop is terminated");
+
+       src = g_idle_source_new();
+       retvm_if(!src, false, "Failed to allocate memory");
+
+       g_source_unref(src);
+
+       /* Not Supported yet */
+       return false;
+}
+
+bool event_loop::remove_event(uint64_t id, bool close_channel)
+{
+       auto it = m_infos.find(id);
+       retv_if(it == m_infos.end(), false);
+
+       if (close_channel)
+               g_io_channel_shutdown(it->second->g_ch, TRUE, NULL);
+
+       release_info(it->second);
+       m_infos.erase(it);
+
+       /* _D("Removed[%llu]", id); */
+       return true;
+}
+
+void event_loop::remove_all_events(void)
+{
+       auto it = m_infos.begin();
+       while (it != m_infos.end()) {
+               release_info(it->second);
+               it = m_infos.erase(it);
+       }
+}
+
+void event_loop::release_info(handler_info *info)
+{
+       ret_if(!info->g_ch || info->id == 0);
+
+       g_source_destroy(info->g_src);
+       g_source_unref(info->g_src);
+
+       g_io_channel_unref(info->g_ch);
+       info->g_ch = NULL;
+
+       delete info->handler;
+       info->handler = NULL;
+
+       delete info;
+}
+
+class terminator : public event_handler
+{
+public:
+       terminator(event_loop *loop)
+       : m_loop(loop)
+       { }
+
+       bool handle(int fd, event_condition condition)
+       {
+               m_loop->terminate();
+               return false;
+       }
+
+private:
+       event_loop *m_loop;
+};
+
+bool event_loop::run(int timeout)
+{
+       retvm_if(!m_mainloop, false, "Invalid GMainLoop");
+       retv_if(is_running(), false);
+
+       if (timeout > 0) {
+               GSource *src = g_timeout_source_new(timeout);
+               g_source_set_callback(src, on_timer, this, NULL);
+               g_source_attach(src, g_main_loop_get_context(m_mainloop));
+               g_source_unref(src);
+       }
+
+       m_term_fd = eventfd(0, 0);
+       retv_if(m_term_fd == -1, false);
+
+       terminator *handler = new(std::nothrow) terminator(this);
+       retvm_if(!handler, false, "Failed to allocate memory");
+
+       add_event(m_term_fd, EVENT_IN | EVENT_HUP | EVENT_NVAL, handler);
+
+       m_running.store(true);
+
+       _I("Started");
+       g_main_loop_run(m_mainloop);
+}
+
+void event_loop::stop(void)
+{
+       ret_if(!is_running() || m_terminating.load());
+
+       uint64_t term = 1;
+       m_terminating.store(true);
+       write(m_term_fd, &term, sizeof(uint64_t));
+}
+
+void event_loop::terminate(void)
+{
+       remove_all_events();
+
+       if (m_mainloop) {
+               g_main_loop_quit(m_mainloop);
+               g_main_loop_unref(m_mainloop);
+               m_mainloop = NULL;
+       }
+
+       m_running.store(false);
+       m_terminating.store(false);
+
+       _I("Terminated");
+}
+
+bool event_loop::is_running(void)
+{
+       return m_running.load();
+}
+
+bool event_loop::is_terminator(int fd)
+{
+       return (m_term_fd == fd);
+}
diff --git a/src/shared/event_loop.h b/src/shared/event_loop.h
new file mode 100644 (file)
index 0000000..934a8b4
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * sensord
+ *
+ * Copyright (c) 2017 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.
+ *
+ */
+
+#ifndef __EVENT_LOOP_H__
+#define __EVENT_LOOP_H__
+
+#include <stdint.h>
+#include <glib.h>
+#include <atomic>
+#include <map>
+
+#include "event_handler.h"
+
+namespace ipc {
+
+enum event_condition_e {
+       EVENT_IN =  G_IO_IN,
+       EVENT_OUT = G_IO_OUT,
+       EVENT_HUP = G_IO_HUP,
+       EVENT_NVAL = G_IO_NVAL,
+};
+
+/* move it to file */
+class idle_handler {
+       virtual ~idle_handler();
+       bool handle(int fd);
+};
+
+class event_loop;
+
+class handler_info {
+public:
+       handler_info(int64_t _id, int _fd, GIOChannel *_ch, GSource *_src, event_handler *_handler, event_loop *_loop)
+       : id(_id)
+       , fd(_fd)
+       , g_ch(_ch)
+       , g_src(_src)
+       , handler(_handler)
+       , loop(_loop)
+       {}
+
+       uint64_t id;
+       int fd;
+       GIOChannel *g_ch;
+       GSource *g_src;
+       event_handler *handler;
+       event_loop *loop;
+};
+
+class event_loop {
+public:
+       typedef unsigned int event_condition;
+       typedef bool (*idle_cb)(void *);
+
+       event_loop();
+       event_loop(GMainLoop *mainloop);
+       ~event_loop();
+
+       uint64_t add_event(const int fd, const event_condition cond, event_handler *handler);
+       uint64_t add_idle_event(unsigned int priority, idle_handler *handler);
+
+       bool remove_event(uint64_t id, bool close_channel = false);
+       void remove_all_events(void);
+
+       void release_info(handler_info *info);
+
+       bool run(int timeout = 0);
+       void stop(void);
+       void terminate(void);
+
+       bool is_running(void);
+       bool is_terminator(int fd);
+
+private:
+       GMainLoop *m_mainloop;
+       std::atomic<bool> m_running;
+       std::atomic<bool> m_terminating;
+       std::atomic<uint64_t> m_sequence;
+       std::map<uint64_t, handler_info *> m_infos;
+
+       int m_term_fd;
+};
+
+}
+
+#endif /* __EVENT_LOOP_H__ */