--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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");
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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