aboutsummaryrefslogtreecommitdiff
path: root/include/cru/base/platform/unix/EventLoop.h
blob: bc2bcbf8c4d7f6ad08a5dfb85dad76297f715bb2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#pragma once

#include <optional>
#if !defined(__unix) && !defined(__APPLE__)
#error "This file can only be included on unix."
#endif

#include "../../Base.h"
#include "../../Exception.h"
#include "UnixFile.h"

#include <unistd.h>
#include <chrono>
#include <thread>

namespace cru::platform::unix {
class UnixTimerFile : public Object2 {
 public:
  template <class Rep, class Period>
  explicit UnixTimerFile(std::chrono::duration<Rep, Period> time) {
    auto fds = OpenUniDirectionalPipe();
    this->read_fd_ = std::move(fds.read);
    this->write_fd_ = std::move(fds.write);

    this->thread_ = std::thread([this, time] {
      std::this_thread::sleep_for(time);
      constexpr auto buffer = "";
      auto written = ::write(this->write_fd_, buffer, 1);
      if (written != 1) {
        throw Exception(
            "Failed to write to pipe in UnixTimerFile thread at timeout.");
      }
    });
    this->thread_.detach();
  }

  int GetReadFd() const;

 private:
  UnixFileDescriptor write_fd_;
  UnixFileDescriptor read_fd_;
  std::thread thread_;
};

class UnixEventLoop : public Object2 {
 public:
  UnixEventLoop();

  int Run();
  void RequestQuit(int exit_code = 0);

  int SetTimer(std::function<void()> action, std::chrono::milliseconds timeout,
               bool repeat);

  int SetImmediate(std::function<void()> action) {
    return this->SetTimer(std::move(action), std::chrono::milliseconds::zero(),
                          false);
  }

  int SetTimeout(std::function<void()> action,
                 std::chrono::milliseconds timeout) {
    return this->SetTimer(std::move(action), std::move(timeout), false);
  }

  int SetInterval(std::function<void()> action,
                  std::chrono::milliseconds interval) {
    return this->SetTimer(std::move(action), std::move(interval), true);
  }

 private:
  struct TimerData {
    int id;
    std::chrono::milliseconds original_timeout;
    std::chrono::milliseconds timeout;
    bool repeat;
    std::function<void()> action;

    TimerData(int id, std::chrono::milliseconds timeout, bool repeat,
              std::function<void()> action)
        : id(id),
          original_timeout(timeout),
          timeout(timeout),
          repeat(repeat),
          action(std::move(action)) {}
  };

  std::thread::id running_thread_;

  std::atomic_int timer_tag_;
  std::vector<TimerData> timers_;

  std::optional<int> exit_code_;
};

}  // namespace cru::platform::unix