1/*
2 * Copyright (C) 2020-2022 The opuntiaOS Project Authors.
3 * + Contributed by Nikita Melekhin <nimelehin@gmail.com>
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include <cstring>
10#include <ctime>
11#include <iostream>
12#include <libfoundation/EventLoop.h>
13#include <libfoundation/Logger.h>
14#include <memory>
15#include <sched.h>
16#include <sys/select.h>
17#include <sys/time.h>
18#include <unistd.h>
19
20namespace LFoundation {
21
22EventLoop* s_LFoundation_EventLoop_the = nullptr;
23
24EventLoop::EventLoop()
25{
26 s_LFoundation_EventLoop_the = this;
27}
28
29void EventLoop::check_fds()
30{
31 if (m_waiting_fds.empty()) {
32 return;
33 }
34 fd_set_t readfds;
35 fd_set_t writefds;
36 FD_ZERO(&readfds);
37 FD_ZERO(&writefds);
38 int nfds = -1;
39 for (int i = 0; i < m_waiting_fds.size(); i++) {
40 if (m_waiting_fds[i].m_on_read) {
41 FD_SET(m_waiting_fds[i].m_fd, &readfds);
42 }
43 if (m_waiting_fds[i].m_on_write) {
44 FD_SET(m_waiting_fds[i].m_fd, &writefds);
45 }
46 if (nfds < m_waiting_fds[i].m_fd) {
47 nfds = m_waiting_fds[i].m_fd;
48 }
49 }
50
51 // For now, that means, that we don't wait for fds.
52 timeval_t timeout;
53 timeout.tv_sec = 0;
54 timeout.tv_usec = 0;
55
56 int res = select(nfds + 1, &readfds, &writefds, nullptr, &timeout);
57
58 for (int i = 0; i < m_waiting_fds.size(); i++) {
59 if (m_waiting_fds[i].m_on_read) {
60 if (FD_ISSET(m_waiting_fds[i].m_fd, &readfds)) {
61 m_event_queue.push_back(QueuedEvent(m_waiting_fds[i], new FDWaiterReadEvent()));
62 }
63 }
64 if (m_waiting_fds[i].m_on_write) {
65 if (FD_ISSET(m_waiting_fds[i].m_fd, &writefds)) {
66 m_event_queue.push_back(QueuedEvent(m_waiting_fds[i], new FDWaiterWriteEvent()));
67 }
68 }
69 }
70}
71
72void EventLoop::check_timers()
73{
74 if (m_timers.empty()) {
75 return;
76 }
77
78 std::timespec tp;
79 clock_gettime(CLOCK_MONOTONIC, &tp);
80
81 for (auto& timer : m_timers) {
82 if (!timer.expired(tp)) {
83 continue;
84 }
85
86 m_event_queue.push_back(QueuedEvent(timer, new TimerEvent()));
87
88 if (timer.repeated()) {
89 timer.reload(tp);
90 }
91 }
92}
93
94[[gnu::flatten]] void EventLoop::pump()
95{
96 check_fds();
97 check_timers();
98 std::vector<QueuedEvent> events_to_dispatch(std::move(m_event_queue));
99 m_event_queue.clear();
100 for (auto& event : events_to_dispatch) {
101 event.receiver.receive_event(std::move(event.event));
102 }
103
104 if (!events_to_dispatch.size()) {
105 sched_yield();
106 }
107}
108
109int EventLoop::run()
110{
111 while (!m_stop_flag) {
112 pump();
113 }
114 return m_exit_code;
115}
116
117} // namespace LFoundation