Event loop implement 87/167287/2
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Fri, 5 Feb 2016 08:57:10 +0000 (11:57 +0300)
committerVyacheslav Cherkashin <v.cherkashin@samsung.com>
Wed, 17 Jan 2018 15:36:39 +0000 (18:36 +0300)
Change-Id: I256a9a700eb5bf1c7fd0a31d85d20011fd17d085
Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
daemon/cpp/events/evloop.cpp [new file with mode: 0644]
daemon/cpp/events/evloop.h [new file with mode: 0644]
daemon/cpp/events/evloop_c.cpp [new file with mode: 0644]
daemon/cpp/events/evloop_c.h [new file with mode: 0644]
daemon/cpp/events/evloop_request.h [new file with mode: 0644]

diff --git a/daemon/cpp/events/evloop.cpp b/daemon/cpp/events/evloop.cpp
new file mode 100644 (file)
index 0000000..4806233
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *      Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * 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.
+ *
+ * Contributors:
+ * - Samsung RnD Institute Russia
+ *
+ */
+
+#include "evloop.h"
+#include <vector>
+#include <algorithm>
+#include <cstring>
+#include <cassert>
+#include <cinttypes>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include "swap_debug.h"
+#include <utils/unique_ptr.h>
+
+
+namespace EvLoop {
+
+
+class Handler
+{
+public:
+    Handler(int fd, void *data, EventLoop::CallBack callback) :
+        fd_(fd), data_(data), callback_(callback)
+    {}
+
+    void do_callback() const
+    {
+        callback_(this, data_);
+    }
+
+    int fd() const
+    {
+        return fd_;
+    }
+
+private:
+    int fd_;
+    void *data_;
+    EventLoop::CallBack callback_;
+};
+
+
+static void epoll_add_handler(int efd, Handler *handler)
+{
+    struct epoll_event event;
+    event.data.ptr = static_cast<void *>(handler);
+    event.events = EPOLLIN | EPOLLERR;
+
+    int ret = epoll_ctl(efd, EPOLL_CTL_ADD, handler->fd(), &event);
+    if (ret == -1)
+        throw std::runtime_error("Failed to add event to epoll, errno=" +
+                                 std::to_string(errno));
+}
+
+static void epoll_del_handler(int efd, Handler *handler)
+{
+    int ret = epoll_ctl(efd, EPOLL_CTL_DEL, handler->fd(), NULL);
+    if (ret == -1)
+        throw std::runtime_error("Failed to remove event from epoll, errno=" +
+                                 std::to_string(errno));
+}
+
+
+class RequestHandlerAdd : public Request {
+public:
+    RequestHandlerAdd(int efd, Handler *handler) :
+        efd_(efd),
+        handler_(handler)
+    {}
+
+    void execute()
+    {
+        LOGI("ADD: handler[%p fd=%d]\n", handler_, handler_->fd());
+        epoll_add_handler(efd_, handler_);
+    }
+
+private:
+    int efd_;
+    Handler *handler_;
+};
+
+class RequestHandlerDel : public Request {
+public:
+    RequestHandlerDel(int efd, SkipList<Handler *> *skip_list,
+                      Handler *handler, void (*deleter)(Handler *handler)) :
+        efd_(efd),
+        skip_list_(skip_list),
+        handler_(handler),
+        deleter_(deleter)
+    {}
+
+    void execute()
+    {
+        skip_list_->add(handler_);
+
+        epoll_del_handler(efd_, handler_);
+        LOGI("DEL: handler[%p fd=%d]\n", handler_, handler_->fd());
+
+        deleter_(handler_);
+    }
+
+private:
+    int efd_;
+    SkipList<Handler *> *skip_list_;
+
+    Handler *handler_;
+    void (*deleter_)(Handler *handler);
+};
+
+
+EventLoop::EventLoop() :
+    self_handler_(nullptr),
+    state_(RS_STOP),
+    handler_cnt_(0)
+{
+    int ev_fd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
+    if (ev_fd == -1)
+        throw std::runtime_error("Failed to create pipe, errno=" + std::to_string(errno));
+
+    efd_ = ::epoll_create1(EPOLL_CLOEXEC);
+    if (efd_ == -1) {
+        std::string err("Failed to create epoll descriptor, errno=" + std::to_string(errno));
+        ::close(ev_fd);
+        throw std::runtime_error(err);
+    }
+
+    try {
+        self_handler_ = new Handler(ev_fd, this, self_handler_callback);
+        epoll_add_handler(efd_, self_handler_);
+    } catch (const std::runtime_error &e) {
+        delete self_handler_;
+        ::close(efd_);
+        ::close(ev_fd);
+        throw e;
+    } catch (...) {
+        delete self_handler_;
+        ::close(efd_);
+        ::close(ev_fd);
+        throw std::runtime_error("EventLoop: unknown error");
+    }
+}
+
+EventLoop::~EventLoop()
+{
+    req_queue_.do_all();
+    if (handler_cnt_)
+        LOGW("%s handlers have not deleted\n", std::to_string(handler_cnt_).c_str());
+
+    delete self_handler_;
+    ::close(efd_);
+
+}
+
+void EventLoop::self_handler_callback(const Handler *handler, void *data)
+{
+    int fd = handler->fd();
+    EventLoop *self = static_cast<EventLoop *>(data);
+    assert(self->self_handler_ == handler);
+
+    eventfd_t ev_val;
+    if (::eventfd_read(fd, &ev_val) == -1)
+        throw std::runtime_error("Cannot read eventfd, fd=" + std::to_string(fd));
+
+    self->req_queue_.do_all();
+}
+
+void EventLoop::handler_weak_up()
+{
+    int fd = self_handler_->fd();
+    eventfd_t ev_val = 1;
+
+    if (::eventfd_write(fd, ev_val)  == -1)
+        throw std::runtime_error("Cannot write eventfd, fd=" + std::to_string(fd));
+}
+
+Handler *EventLoop::handler_add(int fd, void *data, CallBack callback)
+{
+    Handler *handler = new Handler(fd, data, callback);
+    Request *r = new RequestHandlerAdd(efd_, handler);
+    auto req = makeUnique(r);
+
+    req_queue_.push(req);
+    handler_weak_up();
+
+    ++handler_cnt_;
+    LOGI("Add handler[%p fd=%d]\n", handler, handler->fd());
+
+    return handler;
+
+}
+
+void EventLoop::handler_del(Handler *handler)
+{
+    LOGI("Remove handler[%p fd=%d]\n", handler, handler->fd());
+    --handler_cnt_;
+
+    Request *r = new RequestHandlerDel(efd_, &skip_list_, handler, [](Handler *h) { delete h; });
+    auto req = makeUnique(r);
+
+    req_queue_.push(req);
+    handler_weak_up();
+}
+
+void EventLoop::check_events(struct epoll_event *events, int n)
+{
+    for (int i = 0; i < n; ++i) {
+        struct epoll_event *event = &events[i];
+        Handler *handler = static_cast<Handler *>(event->data.ptr);
+
+        if (skip_list_.is_contain(handler)) {
+            LOGI("Skip handler=%p\n", handler);
+            continue;
+        }
+
+        if (event->events & EPOLLERR)
+            LOGE("event[%p fd=%d] epoll error\n", handler, handler->fd());
+        else if (event->events & EPOLLIN)
+            handler->do_callback();
+    }
+
+    skip_list_.clear();
+}
+
+void EventLoop::stop()
+{
+    std::unique_lock<std::mutex> lock(state_mutex_);
+    if (state_ == RS_STOP)
+        return;
+
+    state_ = RS_STOPPING;
+    handler_weak_up();
+
+    cv_.wait(lock, [this] { return state_ == RS_STOP; });
+}
+
+void EventLoop::run()
+{
+    if (state_ != RS_STOP)
+        throw std::runtime_error("Event loop is running");
+
+    const int ev_array_size = 32;
+    struct epoll_event ev_array[ev_array_size];
+
+
+    {
+        std::lock_guard<std::mutex> lock(state_mutex_);
+        state_ = RS_RUN;
+    }
+
+    while (state_ == RS_RUN) {
+        ::memset(&ev_array, 0, sizeof(ev_array_size));
+        int n = epoll_wait(efd_, ev_array, ev_array_size, -1);
+        if (n > 0) {
+            check_events(ev_array, n);
+        } else if (n == -1) {
+            if (errno == EINTR)
+                continue;
+
+            LOGE("Failed to epoll_wait() call, errno=%d\n", errno);
+        }
+    }
+
+    {
+        std::lock_guard<std::mutex> lock(state_mutex_);
+        state_ = RS_STOP;
+    }
+    cv_.notify_one();
+}
+
+
+} // namespace EvLoop
diff --git a/daemon/cpp/events/evloop.h b/daemon/cpp/events/evloop.h
new file mode 100644 (file)
index 0000000..08f865e
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *      Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * 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.
+ *
+ * Contributors:
+ * - Samsung RnD Institute Russia
+ *
+ */
+
+
+#ifndef EVLOOP_H
+#define EVLOOP_H
+
+
+#include <list>
+#include <mutex>
+#include <memory>
+#include <atomic>
+#include <algorithm>
+#include <condition_variable>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+#include "evloop_request.h"
+
+
+namespace EvLoop {
+
+
+template <typename Item>
+class SkipList
+{
+public:
+    void add(Item item)
+    {
+        std::lock_guard<std::mutex> lock(mutex_);
+        cont_.push_back(item);
+    }
+
+    bool is_contain(Item item)
+    {
+        std::lock_guard<std::mutex> lock(mutex_);
+        auto end = cont_.end();
+        auto it = std::find(cont_.begin(), end, item);
+
+        return it != end;
+    }
+
+    void clear()
+    {
+        std::lock_guard<std::mutex> lock(mutex_);
+        cont_.clear();
+    }
+
+private:
+    std::mutex mutex_;
+    std::vector<Item> cont_;
+};
+
+
+class Handler;
+
+
+class EventLoop
+{
+public:
+    using CallBack = void (*)(const Handler *handler, void *data);
+
+    EventLoop();
+    ~EventLoop();
+
+    Handler *handler_add(int fd, void *data, CallBack callback);
+    void handler_del(Handler *handler);
+
+    void run();
+    void stop();
+
+private:
+    static void self_handler_callback(const Handler *handler, void *data);
+    void handler_weak_up();
+    void check_events(struct epoll_event *events, int n);
+
+    enum RunnintState {
+        RS_RUN,
+        RS_STOPPING,
+        RS_STOP,
+    };
+
+    int efd_;
+    Handler *self_handler_;
+    SkipList<Handler *> skip_list_;
+
+    std::mutex state_mutex_;
+    RunnintState state_;
+    std::condition_variable cv_;
+
+    std::atomic_int handler_cnt_;
+    RequestQueue req_queue_;
+};
+
+
+} // namespace EvLoop
+
+
+#endif // EVLOOP_H
diff --git a/daemon/cpp/events/evloop_c.cpp b/daemon/cpp/events/evloop_c.cpp
new file mode 100644 (file)
index 0000000..88d7b20
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *      Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * 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.
+ *
+ * Contributors:
+ * - Samsung RnD Institute Russia
+ *
+ */
+
+#include "evloop_c.h"
+#include "evloop.h"
+#include "swap_debug.h"
+
+
+struct evloop {
+    EvLoop::EventLoop loop;
+};
+
+struct evloop_handler {
+};
+
+struct evloop *evloop_create(void)
+{
+    struct evloop *loop = nullptr;
+
+    try {
+        loop = new evloop();
+    } catch (const std::runtime_error &e) {
+        LOGE("%s\n", e.what());
+    } catch (...) {
+        LOGE("Unknown error\n");
+    }
+
+    return loop;
+}
+
+void evloop_destroy(struct evloop *loop)
+{
+    delete loop;
+}
+
+struct evloop_handler *evloop_handler_add(struct evloop *loop,
+                                          int fd, void *data,
+                                          evloop_callback_t callback)
+{
+    EvLoop::Handler *handler = nullptr;
+
+    try {
+        auto cb = reinterpret_cast<EvLoop::EventLoop::CallBack>(callback);
+
+        handler = loop->loop.handler_add(fd, data, cb);
+    } catch (const std::runtime_error &e) {
+        LOGE("%s\n", e.what());
+    } catch (...) {
+        LOGE("Unknown error\n");
+    }
+
+    return reinterpret_cast<evloop_handler *>(handler);
+}
+
+void evloop_handler_del(struct evloop *loop, struct evloop_handler *handler)
+{
+    auto hd = reinterpret_cast<EvLoop::Handler *>(handler);
+
+    try {
+        loop->loop.handler_del(hd);
+    } catch (const std::runtime_error &e) {
+        LOGE("%s\n", e.what());
+    } catch (...) {
+        LOGE("Unknown error\n");
+    }
+}
+
+void evloop_run(struct evloop *loop)
+{
+    try {
+        loop->loop.run();
+    } catch (const std::runtime_error &e) {
+        LOGE("%s\n", e.what());
+    } catch (...) {
+        LOGE("Unknown error\n");
+    }
+}
+
+void evloop_stop(struct evloop *loop)
+{
+    try {
+        loop->loop.stop();
+    } catch (const std::runtime_error &e) {
+        LOGE("%s\n", e.what());
+    } catch (...) {
+        LOGE("Unknown error\n");
+    }
+}
diff --git a/daemon/cpp/events/evloop_c.h b/daemon/cpp/events/evloop_c.h
new file mode 100644 (file)
index 0000000..14f559f
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *      Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * 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.
+ *
+ * Contributors:
+ * - Samsung RnD Institute Russia
+ *
+ */
+
+
+#ifndef EVLOOP_C_H
+#define EVLOOP_C_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct evloop;
+struct evloop_handler;
+typedef void (*evloop_callback_t)(const struct evloop_handler *handler, void *data);
+
+struct evloop *evloop_create(void);
+void evloop_destroy(struct evloop *loop);
+
+struct evloop_handler *evloop_handler_add(struct evloop *loop,
+                                          int fd, void *data,
+                                          evloop_callback_t callback);
+void evloop_handler_del(struct evloop *loop, struct evloop_handler *handler);
+
+void evloop_run(struct evloop *loop);
+void evloop_stop(struct evloop *loop);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // EVLOOP_C_H
diff --git a/daemon/cpp/events/evloop_request.h b/daemon/cpp/events/evloop_request.h
new file mode 100644 (file)
index 0000000..c882c14
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *      Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * 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.
+ *
+ * Contributors:
+ * - Samsung RnD Institute Russia
+ *
+ */
+
+
+#ifndef EVLOOP_REQUEST_H
+#define EVLOOP_REQUEST_H
+
+
+#include <queue>
+#include <memory>
+
+
+namespace EvLoop {
+
+
+class Request {
+public:
+    virtual ~Request() {}
+    virtual void execute() = 0;
+};
+
+
+class RequestQueue {
+public:
+    void push(std::unique_ptr<Request> &req)
+    {
+        std::lock_guard<std::mutex> lock(req_queue_mutex_);
+        req_queue_.push(std::move(req));
+    }
+
+    void do_all()
+    {
+        req_queue_mutex_.lock();
+        while (!req_queue_.empty()) {
+            auto req = std::move(req_queue_.front());
+            req_queue_.pop();
+            req_queue_mutex_.unlock();
+
+            req->execute();
+
+            req_queue_mutex_.lock();
+        }
+        req_queue_mutex_.unlock();
+    }
+
+    bool empty()
+    {
+        std::lock_guard<std::mutex> lock(req_queue_mutex_);
+        return req_queue_.empty();
+    }
+
+private:
+    std::queue<std::unique_ptr<Request>> req_queue_;
+    std::mutex req_queue_mutex_;
+};
+
+
+} // namespace EvLoop
+
+#endif // EVLOOP_REQUEST_H