tin  1.5.9
kevent.h
Go to the documentation of this file.
1 // Copyright (c) 2026 Tinverse LLC. All rights reserved.
2 // SPDX-License-Identifier: LicenseRef-Tinverse-Commercial
3 
11 
12 #pragma once
13 
14 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
15  defined(__NetBSD__)
16 
17 #include <atomic>
18 #include <cstddef>
19 #include <cstdint>
20 #include <sys/event.h>
21 #include <sys/time.h>
22 #include <thread>
23 #include <unistd.h>
24 #include <utility>
25 
26 #include "tsm/runtime/executor.h"
27 
28 namespace tsm {
29 
30 template<typename... Tasks>
31 class kevent_task_executor
32 {
33  public:
34  explicit kevent_task_executor(Tasks&... tasks)
35  : ready_(tasks...)
36  {
37  queue_fd_ = kqueue();
38  if (queue_fd_ >= 0) {
39  struct kevent event
40  {};
41  EV_SET(&event, 1, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, nullptr);
42  static_cast<void>(
43  kevent(queue_fd_, &event, 1, nullptr, 0, nullptr));
44  }
45  }
46 
47  kevent_task_executor(kevent_task_executor const&) = delete;
48  kevent_task_executor& operator=(kevent_task_executor const&) = delete;
49 
50  ~kevent_task_executor()
51  {
52  stop();
53  if (queue_fd_ >= 0) {
54  close(queue_fd_);
55  }
56  }
57 
58  void start()
59  {
60  if (worker_.joinable()) {
61  return;
62  }
63  stop_requested_.store(false);
64  worker_ = std::thread([this] {
65  while (!stop_requested_.load()) {
66  if (ready_.run_ready() == 0U) {
67  wait_for_work();
68  }
69  }
70  });
71  }
72 
73  void stop()
74  {
75  stop_requested_.store(true);
76  wake();
77  if (worker_.joinable()) {
78  worker_.join();
79  }
80  }
81 
82  void wake()
83  {
84  if (queue_fd_ < 0) {
85  return;
86  }
87  // EVFILT_USER gives the executor a portable BSD/macOS wake signal
88  // without coupling runtime queues to kqueue.
89  struct kevent event
90  {};
91  EV_SET(&event, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, nullptr);
92  static_cast<void>(kevent(queue_fd_, &event, 1, nullptr, 0, nullptr));
93  }
94 
95  void wake_from_isr()
96  {
97  wake();
98  }
99 
100  void wait_for_work()
101  {
102  if (queue_fd_ < 0) {
103  return;
104  }
105  // The timeout keeps shutdown responsive; normal work arrives through
106  // wake(), which triggers the user event immediately.
107  struct kevent event
108  {};
109  struct timespec timeout
110  {
111  0, 1000000
112  };
113  static_cast<void>(kevent(queue_fd_, nullptr, 0, &event, 1, &timeout));
114  }
115 
116  [[nodiscard]] bool step()
117  {
118  return ready_.step();
119  }
120  std::size_t run_ready()
121  {
122  return ready_.run_ready();
123  }
124 
125  std::size_t tick(tsm::tick_rep elapsed_ticks = 1U)
126  {
127  const auto resumed = ready_.tick(elapsed_ticks);
128  if (resumed != 0U) {
129  wake();
130  }
131  return resumed;
132  }
133 
134  std::size_t tick(tsm::tick_count elapsed_ticks)
135  {
136  return tick(elapsed_ticks.count());
137  }
138 
139  void start_all()
140  {
141  ready_.start_all();
142  }
143 
144  template<auto Entry, std::size_t Instance = 0U>
145  [[nodiscard]] spawn_result start()
146  {
147  auto result = ready_.template start<Entry, Instance>();
148  if (result == spawn_result::started) {
149  wake();
150  }
151  return result;
152  }
153 
154  template<auto Entry, typename... Args>
155  [[nodiscard]] spawn_result spawn(Args&&... args)
156  {
157  auto result = ready_.template spawn<Entry>(std::forward<Args>(args)...);
158  if (result == spawn_result::started) {
159  wake();
160  }
161  return result;
162  }
163 
164  template<auto Entry, std::size_t Instance = 0U>
165  [[nodiscard]] task_status task_status() const
166  {
167  return ready_.template task_status<Entry, Instance>();
168  }
169 
170  template<auto Entry, std::size_t Instance = 0U>
171  [[nodiscard]] task_failure_reason task_failure_reason() const
172  {
173  return ready_.template task_failure_reason<Entry, Instance>();
174  }
175 
176  template<auto Entry, std::size_t Instance = 0U>
177  [[nodiscard]] bool cancel() noexcept
178  {
179  const bool cancelled = ready_.template cancel<Entry, Instance>();
180  if (cancelled) {
181  wake();
182  }
183  return cancelled;
184  }
185 
186  void cancel_all() noexcept
187  {
188  ready_.cancel_all();
189  wake();
190  }
191 
192  private:
193  runtime::cooperative_executor<Tasks...> ready_;
194  std::thread worker_{};
195  std::atomic_bool stop_requested_{ true };
196  int queue_fd_{ -1 };
197 };
198 
199 template<typename... Tasks>
200 kevent_task_executor(Tasks&...) -> kevent_task_executor<Tasks...>;
201 
202 template<typename... Tasks>
203 using task_executor = kevent_task_executor<Tasks...>;
204 
205 } // namespace tsm
206 
207 #endif
Target-independent executors for tsm runtimes.
tsm::timeout_ticks_awaitable timeout(tick_domain< TickPeriod > domain, Duration duration) noexcept
Definition: chrono_ticks.h:119
cooperative_executor(Tasks &...) -> cooperative_executor< Tasks... >
Definition: bare_metal.h:20
task_failure_reason
Definition: coroutine.h:170
task_status
Definition: coroutine.h:160
bare_metal_task_executor< Tasks... > task_executor
Definition: bare_metal.h:166
spawn_result
Definition: coroutine.h:181
TSM_TICK_REP tick_rep
Definition: ticks.h:35
Strong value type for semantic scheduler ticks.
Definition: ticks.h:54
constexpr tick_rep count() const noexcept
Definition: ticks.h:73