tin  1.5.9
sync.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 
12 
13 #pragma once
14 
15 #include <algorithm>
16 #include <array>
17 #include <concepts>
18 #include <cstddef>
19 #include <cstdint>
20 #include <type_traits>
21 #include <utility>
22 
23 #include "tsm/runtime/coroutine.h"
24 #include "tsm/runtime/policy.h"
25 
26 namespace tsm {
27 
28 namespace runtime::detail {
29 
30 template<std::size_t MaxWaiters>
31 class waiter_list
32 {
33  public:
34  [[nodiscard]] bool add(task_context& context) noexcept
35  {
36  std::replace_if(
37  waiters_.begin(),
38  waiters_.end(),
39  [](auto const& waiter) {
40  return waiter.context != nullptr &&
41  waiter.context->generation() != waiter.generation;
42  },
43  waiter_record{});
44  // Re-registering the same waiter is harmless. This matters when a task
45  // observes a primitive more than once before the scheduler resumes it.
46  if (std::any_of(
47  waiters_.begin(), waiters_.end(), [&context](auto const& waiter) {
48  return waiter.context == &context &&
49  waiter.generation == context.generation();
50  })) {
51  return true;
52  }
53  auto slot = std::find_if(
54  waiters_.begin(), waiters_.end(), [](auto const& waiter) {
55  return waiter.context == nullptr;
56  });
57  if (slot != waiters_.end()) {
58  *slot = waiter_record{ &context, context.generation() };
59  return true;
60  }
61  // Waiter overflow is a configuration error for static systems. Marking
62  // the task failed makes the failure visible without throwing.
63  context.mark_failed(task_failure_reason::wait_queue_full);
64  return false;
65  }
66 
67  void remove(task_context& context) noexcept
68  {
69  std::replace_if(
70  waiters_.begin(),
71  waiters_.end(),
72  [&context](auto const& waiter) { return waiter.context == &context; },
73  waiter_record{});
74  }
75 
76  void wake_one() noexcept
77  {
78  // Wake order is stable by waiter-slot order. It is not a priority
79  // mechanism; scheduling fairness is handled by the task ready queue.
80  for (auto& waiter : waiters_) {
81  if (waiter.context == nullptr) {
82  continue;
83  }
84  if (waiter.context->generation() == waiter.generation) {
85  waiter.context->mark_ready();
86  }
87  waiter = {};
88  return;
89  }
90  }
91 
92  void wake_all() noexcept
93  {
94  for (auto& waiter : waiters_) {
95  if (waiter.context != nullptr) {
96  if (waiter.context->generation() == waiter.generation) {
97  waiter.context->mark_ready();
98  }
99  waiter = {};
100  }
101  }
102  }
103 
104  private:
105  struct waiter_record
106  {
107  task_context* context{};
108  std::uint32_t generation{};
109  };
110 
111  std::array<waiter_record, MaxWaiters> waiters_{};
112 };
113 
114 } // namespace runtime::detail
115 
120 template<std::size_t MaxWaiters = 4U>
122 {
123  public:
124  void set() noexcept
125  {
126  set_ = true;
127  waiters_.wake_all();
128  }
129 
130  void set_from_isr() noexcept
131  {
132  set();
133  }
134 
135  void reset() noexcept
136  {
137  set_ = false;
138  }
139  [[nodiscard]] bool is_set() const noexcept
140  {
141  return set_;
142  }
143 
145  {
146  public:
147  explicit wait_awaitable(event_flag& flag) noexcept
148  : flag_(&flag)
149  {
150  }
151 
152  [[nodiscard]] bool await_ready() const noexcept
153  {
154  return flag_->is_set();
155  }
156 
157  void await_suspend(task::handle_type handle) noexcept
158  {
159  (void)flag_->waiters_.add(handle.promise().context());
160  }
161 
162  void await_cancel(task_context& context) noexcept
163  {
164  flag_->waiters_.remove(context);
165  }
166 
167  void await_resume() const noexcept {}
168 
169  private:
170  event_flag* flag_{};
171  };
172 
173  [[nodiscard]] wait_awaitable wait() noexcept
174  {
175  return wait_awaitable{ *this };
176  }
177 
178  private:
179  bool set_{};
180  runtime::detail::waiter_list<MaxWaiters> waiters_{};
181 };
182 
189 template<std::size_t MaxWaiters = 4U>
191 {
192  public:
193  void notify() noexcept
194  {
195  flag_.set();
196  }
197  void notify_from_isr() noexcept
198  {
199  flag_.set();
200  }
201  void reset() noexcept
202  {
203  flag_.reset();
204  }
205  [[nodiscard]] bool is_set() const noexcept
206  {
207  return flag_.is_set();
208  }
209 
210  [[nodiscard]] auto wait() noexcept
211  {
212  return flag_.wait();
213  }
214 
215  private:
216  event_flag<MaxWaiters> flag_{};
217 };
218 
219 template<std::size_t MaxWaiters = 4U>
221 
222 template<std::size_t MaxWaiters>
223 class cancellation_source;
224 
226 template<std::size_t MaxWaiters = 4U>
228 {
229  public:
230  cancellation_token() = default;
231 
232  [[nodiscard]] bool stop_requested() const noexcept
233  {
234  return source_ != nullptr && source_->stop_requested();
235  }
236 
238  {
239  public:
240  explicit wait_awaitable(
241  cancellation_source<MaxWaiters>& source) noexcept
242  : source_(&source)
243  {
244  }
245 
246  [[nodiscard]] bool await_ready() const noexcept
247  {
248  return source_->stop_requested();
249  }
250 
251  void await_suspend(task::handle_type handle) noexcept
252  {
253  (void)source_->waiters_.add(handle.promise().context());
254  }
255 
256  void await_cancel(task_context& context) noexcept
257  {
258  source_->waiters_.remove(context);
259  }
260 
261  void await_resume() const noexcept {}
262 
263  private:
265  };
266 
267  [[nodiscard]] wait_awaitable wait() const noexcept
268  {
269  return wait_awaitable{ *source_ };
270  }
271 
272  private:
273  friend class cancellation_source<MaxWaiters>;
274 
275  explicit cancellation_token(
276  cancellation_source<MaxWaiters>& source) noexcept
277  : source_(&source)
278  {
279  }
280 
282 };
283 
289 template<std::size_t MaxWaiters = 4U>
291 {
292  public:
293  [[nodiscard]] cancellation_token<MaxWaiters> token() noexcept
294  {
295  return cancellation_token<MaxWaiters>{ *this };
296  }
297 
298  void request_cancel() noexcept
299  {
300  requested_ = true;
301  waiters_.wake_all();
302  }
303 
304  void reset() noexcept
305  {
306  requested_ = false;
307  }
308 
309  [[nodiscard]] bool stop_requested() const noexcept
310  {
311  return requested_;
312  }
313 
314  private:
315  friend class cancellation_token<MaxWaiters>;
316 
317  bool requested_{};
318  runtime::detail::waiter_list<MaxWaiters> waiters_{};
319 };
320 
325 template<typename T, std::size_t MaxWaiters = 4U>
326 class signal
327 {
328  public:
329  void publish(T const& value)
330  {
331  value_ = value;
332  ready_ = true;
333  waiters_.wake_all();
334  }
335 
336  void publish(T&& value)
337  {
338  value_ = std::move(value);
339  ready_ = true;
340  waiters_.wake_all();
341  }
342 
343  void publish_from_isr(T const& value)
344  {
345  publish(value);
346  }
347 
349  {
350  publish(std::move(value));
351  }
352 
353  [[nodiscard]] bool ready() const noexcept
354  {
355  return ready_;
356  }
357  [[nodiscard]] T const& value() const noexcept
358  {
359  return value_;
360  }
361 
363  {
364  public:
365  explicit wait_awaitable(signal& source) noexcept
366  : source_(&source)
367  {
368  }
369 
370  [[nodiscard]] bool await_ready() const noexcept
371  {
372  return source_->ready();
373  }
374 
375  void await_suspend(task::handle_type handle) noexcept
376  {
377  (void)source_->waiters_.add(handle.promise().context());
378  }
379 
380  void await_cancel(task_context& context) noexcept
381  {
382  source_->waiters_.remove(context);
383  }
384 
385  [[nodiscard]] T await_resume() const
386  {
387  return source_->value_;
388  }
389 
390  private:
391  signal* source_{};
392  };
393 
394  [[nodiscard]] wait_awaitable wait() noexcept
395  {
396  return wait_awaitable{ *this };
397  }
398 
399  private:
400  T value_{};
401  bool ready_{};
402  runtime::detail::waiter_list<MaxWaiters> waiters_{};
403 };
404 
405 namespace runtime {
406 
407 template<typename T>
408 class sender
409 {
410  public:
411  sender() = default;
412 
413  template<typename Channel>
414  explicit sender(Channel& channel) noexcept
415  : object_(&channel)
416  , send_const_([](void* object, T const& value) {
417  return static_cast<Channel*>(object)->try_send(value);
418  })
419  , send_move_([](void* object, T&& value) {
420  return static_cast<Channel*>(object)->try_send(std::move(value));
421  })
422  {
423  }
424 
425  [[nodiscard]] bool try_send(T const& value) const
426  {
427  return send_const_ != nullptr && send_const_(object_, value);
428  }
429 
430  [[nodiscard]] bool try_send(T&& value) const
431  {
432  return send_move_ != nullptr && send_move_(object_, std::move(value));
433  }
434 
435  private:
436  using send_const_fn = bool (*)(void*, T const&);
437  using send_move_fn = bool (*)(void*, T&&);
438 
439  void* object_{};
440  send_const_fn send_const_{};
441  send_move_fn send_move_{};
442 };
443 
444 template<typename T>
445 class receiver
446 {
447  public:
448  receiver() = default;
449 
450  template<typename Channel>
451  explicit receiver(Channel& channel) noexcept
452  : object_(&channel)
453  , receive_([](void* object, T& value) {
454  return static_cast<Channel*>(object)->try_receive(value);
455  })
456  {
457  }
458 
459  [[nodiscard]] bool try_receive(T& value) const
460  {
461  return receive_ != nullptr && receive_(object_, value);
462  }
463 
464  private:
465  using receive_fn = bool (*)(void*, T&);
466 
467  void* object_{};
468  receive_fn receive_{};
469 };
470 
471 template<typename T>
473 {
474  public:
475  latest_reader() = default;
476 
477  template<typename Channel>
478  explicit latest_reader(Channel const& channel) noexcept
479  : object_(&channel)
480  , latest_([](void const* object, T& value) {
481  return static_cast<Channel const*>(object)->latest(value);
482  })
483  {
484  }
485 
486  [[nodiscard]] bool latest(T& value) const
487  {
488  return latest_ != nullptr && latest_(object_, value);
489  }
490 
491  private:
492  using latest_fn = bool (*)(void const*, T&);
493 
494  void const* object_{};
495  latest_fn latest_{};
496 };
497 
502 template<typename T,
503  std::size_t Capacity,
504  typename OverflowPolicy = overflow::reject_newest,
505  std::size_t MaxWaiters = 4U>
506 class channel
507 {
508  static_assert(Capacity > 0U, "tsm: channel requires non-zero capacity");
509  static_assert(overflow_policy<OverflowPolicy>,
510  "tsm: channel requires a known overflow policy");
511 
512  public:
513  using value_type = T;
514  using overflow_policy = OverflowPolicy;
515  static constexpr std::size_t static_capacity = Capacity;
516 
517  [[nodiscard]] bool try_send(T const& value)
518  {
519  const bool accepted = push(value);
520  if (accepted) {
521  latest_ = data_[latest_index()];
522  has_latest_ = true;
523  receivers_.wake_one();
524  }
525  return accepted;
526  }
527 
528  [[nodiscard]] bool try_send(T&& value)
529  {
530  const bool accepted = push(std::move(value));
531  if (accepted) {
532  latest_ = data_[latest_index()];
533  has_latest_ = true;
534  receivers_.wake_one();
535  }
536  return accepted;
537  }
538 
539  [[nodiscard]] bool try_send_from_isr(T const& value)
540  {
541  return try_send(value);
542  }
543 
544  [[nodiscard]] bool try_send_from_isr(T&& value)
545  {
546  return try_send(std::move(value));
547  }
548 
549  [[nodiscard]] bool try_receive(T& value)
550  {
551  if (empty()) {
552  return false;
553  }
554  // Receive transfers from the fixed ring slot into caller-owned storage.
555  // The slot is reused by later sends.
556  value = std::move(data_[pop_index_]);
557  pop_index_ = next(pop_index_);
558  --size_;
559  return true;
560  }
561 
562  [[nodiscard]] bool empty() const noexcept
563  {
564  return size_ == 0U;
565  }
566  [[nodiscard]] bool full() const noexcept
567  {
568  return size_ == Capacity;
569  }
570  [[nodiscard]] std::size_t size() const noexcept
571  {
572  return size_;
573  }
574  [[nodiscard]] static constexpr std::size_t capacity() noexcept
575  {
576  return Capacity;
577  }
578 
579  [[nodiscard]] bool latest(T& value) const
580  {
581  if (!has_latest_) {
582  return false;
583  }
584  value = latest_;
585  return true;
586  }
587 
588  [[nodiscard]] runtime::sender<T> sender() noexcept
589  {
590  return runtime::sender<T>{ *this };
591  }
592 
593  [[nodiscard]] runtime::receiver<T> receiver() noexcept
594  {
595  return runtime::receiver<T>{ *this };
596  }
597 
598  [[nodiscard]] runtime::latest_reader<T> latest_reader() const noexcept
599  {
600  return runtime::latest_reader<T>{ *this };
601  }
602 
603  template<typename Value>
605  {
606  public:
607  send_awaitable(channel& target, Value&& value)
608  : target_(&target)
609  , value_(std::forward<Value>(value))
610  {
611  }
612 
613  [[nodiscard]] bool await_ready()
614  {
615  // Send is a non-blocking awaitable: it completes immediately with
616  // an accepted/rejected result so backpressure is explicit.
617  accepted_ = target_->try_send(std::move(value_));
618  return true;
619  }
620 
622  [[nodiscard]] bool await_resume() const noexcept
623  {
624  return accepted_;
625  }
626 
627  private:
628  channel* target_{};
629  T value_;
630  bool accepted_{};
631  };
632 
633  template<typename Value>
634  [[nodiscard]] auto send(Value&& value)
635  {
636  return send_awaitable<Value>{ *this, std::forward<Value>(value) };
637  }
638 
640  {
641  public:
642  explicit receive_awaitable(channel& source) noexcept
643  : source_(&source)
644  {
645  }
646 
647  [[nodiscard]] bool await_ready()
648  {
649  has_value_ = source_->try_receive(value_);
650  return has_value_;
651  }
652 
653  void await_suspend(task::handle_type handle) noexcept
654  {
655  // Empty receives suspend the task. A later successful send wakes
656  // one receiver through the scheduler ready queue.
657  (void)source_->receivers_.add(handle.promise().context());
658  }
659 
660  void await_cancel(task_context& context) noexcept
661  {
662  source_->receivers_.remove(context);
663  }
664 
665  [[nodiscard]] T await_resume()
666  {
667  if (!has_value_) {
668  // The value may have been provided by the send that woke this
669  // task; take it now that the coroutine is running again.
670  has_value_ = source_->try_receive(value_);
671  }
672  return std::move(value_);
673  }
674 
675  private:
676  channel* source_{};
677  T value_{};
678  bool has_value_{};
679  };
680 
681  [[nodiscard]] receive_awaitable receive() noexcept
682  {
683  return receive_awaitable{ *this };
684  }
685 
686  private:
687  static constexpr std::size_t next(std::size_t index) noexcept
688  {
689  return (index + 1U) % Capacity;
690  }
691 
692  static constexpr std::size_t previous(std::size_t index) noexcept
693  {
694  return index == 0U ? Capacity - 1U : index - 1U;
695  }
696 
697  [[nodiscard]] std::size_t latest_index() const noexcept
698  {
699  return previous(push_index_);
700  }
701 
702  template<typename Value>
703  [[nodiscard]] bool push(Value&& value)
704  {
705  if (full()) {
706  if constexpr (std::same_as<OverflowPolicy,
708  return false;
709  } else if constexpr (std::same_as<OverflowPolicy,
711  pop_index_ = next(pop_index_);
712  --size_;
713  } else {
714  data_[previous(push_index_)] = std::forward<Value>(value);
715  return true;
716  }
717  }
718  // The channel owns a copy of each payload. Transport-specific zero-copy
719  // storage belongs in runtime adapters, not in this cooperative
720  // primitive.
721  data_[push_index_] = std::forward<Value>(value);
722  push_index_ = next(push_index_);
723  ++size_;
724  return true;
725  }
726 
727  std::array<T, Capacity> data_{};
728  T latest_{};
729  std::size_t push_index_{};
730  std::size_t pop_index_{};
731  std::size_t size_{};
732  bool has_latest_{};
733  runtime::detail::waiter_list<MaxWaiters> receivers_{};
734 };
735 
736 } // namespace runtime
737 
738 template<typename T,
739  std::size_t Capacity,
740  typename Overflow = overflow::reject_newest,
741  std::size_t MaxWaiters = 4U>
743 
749 template<typename T, std::size_t MaxWaiters = 4U>
750 class mutex
751 {
752  public:
753  template<typename... Args>
754  explicit mutex(Args&&... args)
755  : value_(std::forward<Args>(args)...)
756  {
757  }
758 
759  mutex() = default;
760 
762  {
763  public:
764  explicit lock_awaitable(mutex& target) noexcept
765  : target_(&target)
766  {
767  }
768 
769  [[nodiscard]] bool await_ready() noexcept
770  {
771  if (!target_->locked_) {
772  target_->locked_ = true;
773  owns_ = true;
774  return true;
775  }
776  return false;
777  }
778 
779  void await_suspend(task::handle_type handle) noexcept
780  {
781  (void)target_->waiters_.add(handle.promise().context());
782  }
783 
784  void await_cancel(task_context& context) noexcept
785  {
786  target_->waiters_.remove(context);
787  }
788 
789  [[nodiscard]] T& await_resume() noexcept
790  {
791  if (!owns_) {
792  // A woken waiter becomes owner when it resumes. The primitive
793  // remains cooperative; no OS mutex or preemptive wait is used.
794  target_->locked_ = true;
795  }
796  return target_->value_;
797  }
798 
799  private:
800  mutex* target_{};
801  bool owns_{};
802  };
803 
804  [[nodiscard]] lock_awaitable lock() noexcept
805  {
806  return lock_awaitable{ *this };
807  }
808 
809  void unlock() noexcept
810  {
811  if (!locked_) {
812  return;
813  }
814  locked_ = false;
815  waiters_.wake_one();
816  }
817 
818  template<typename Fn>
819  decltype(auto) with_lock(Fn&& fn)
820  {
821  return std::forward<Fn>(fn)(value_);
822  }
823 
824  [[nodiscard]] bool locked() const noexcept
825  {
826  return locked_;
827  }
828  [[nodiscard]] T& unsafe_value() noexcept
829  {
830  return value_;
831  }
832  [[nodiscard]] T const& unsafe_value() const noexcept
833  {
834  return value_;
835  }
836 
837  private:
838  T value_{};
839  bool locked_{};
840  runtime::detail::waiter_list<MaxWaiters> waiters_{};
841 };
842 
843 } // namespace tsm
Definition: sync.h:291
cancellation_token< MaxWaiters > token() noexcept
Definition: sync.h:293
void reset() noexcept
Definition: sync.h:304
bool stop_requested() const noexcept
Definition: sync.h:309
void request_cancel() noexcept
Definition: sync.h:298
void await_resume() const noexcept
Definition: sync.h:261
bool await_ready() const noexcept
Definition: sync.h:246
void await_suspend(task::handle_type handle) noexcept
Definition: sync.h:251
void await_cancel(task_context &context) noexcept
Definition: sync.h:256
wait_awaitable(cancellation_source< MaxWaiters > &source) noexcept
Definition: sync.h:240
Read-only cancellation capability for cooperative tasks.
Definition: sync.h:228
wait_awaitable wait() const noexcept
Definition: sync.h:267
friend class cancellation_source< MaxWaiters >
Definition: sync.h:273
bool stop_requested() const noexcept
Definition: sync.h:232
Definition: sync.h:145
void await_suspend(task::handle_type handle) noexcept
Definition: sync.h:157
void await_cancel(task_context &context) noexcept
Definition: sync.h:162
void await_resume() const noexcept
Definition: sync.h:167
wait_awaitable(event_flag &flag) noexcept
Definition: sync.h:147
bool await_ready() const noexcept
Definition: sync.h:152
Definition: sync.h:122
void set() noexcept
Definition: sync.h:124
void reset() noexcept
Definition: sync.h:135
bool is_set() const noexcept
Definition: sync.h:139
void set_from_isr() noexcept
Definition: sync.h:130
wait_awaitable wait() noexcept
Definition: sync.h:173
Definition: sync.h:191
bool is_set() const noexcept
Definition: sync.h:205
auto wait() noexcept
Definition: sync.h:210
void notify_from_isr() noexcept
Definition: sync.h:197
void reset() noexcept
Definition: sync.h:201
void notify() noexcept
Definition: sync.h:193
Definition: sync.h:762
lock_awaitable(mutex &target) noexcept
Definition: sync.h:764
T & await_resume() noexcept
Definition: sync.h:789
void await_cancel(task_context &context) noexcept
Definition: sync.h:784
void await_suspend(task::handle_type handle) noexcept
Definition: sync.h:779
bool await_ready() noexcept
Definition: sync.h:769
Definition: sync.h:751
decltype(auto) with_lock(Fn &&fn)
Definition: sync.h:819
void unlock() noexcept
Definition: sync.h:809
T & unsafe_value() noexcept
Definition: sync.h:828
mutex()=default
bool locked() const noexcept
Definition: sync.h:824
mutex(Args &&... args)
Definition: sync.h:754
lock_awaitable lock() noexcept
Definition: sync.h:804
T const & unsafe_value() const noexcept
Definition: sync.h:832
T await_resume()
Definition: sync.h:665
receive_awaitable(channel &source) noexcept
Definition: sync.h:642
void await_suspend(task::handle_type handle) noexcept
Definition: sync.h:653
bool await_ready()
Definition: sync.h:647
void await_cancel(task_context &context) noexcept
Definition: sync.h:660
void await_suspend(task::handle_type) noexcept
Definition: sync.h:621
bool await_ready()
Definition: sync.h:613
bool await_resume() const noexcept
Definition: sync.h:622
send_awaitable(channel &target, Value &&value)
Definition: sync.h:607
Definition: sync.h:507
OverflowPolicy overflow_policy
Definition: sync.h:514
auto send(Value &&value)
Definition: sync.h:634
static constexpr std::size_t static_capacity
Definition: sync.h:515
runtime::sender< T > sender() noexcept
Definition: sync.h:588
bool try_send(T const &value)
Definition: sync.h:517
bool try_send_from_isr(T const &value)
Definition: sync.h:539
static constexpr std::size_t capacity() noexcept
Definition: sync.h:574
bool try_send(T &&value)
Definition: sync.h:528
bool empty() const noexcept
Definition: sync.h:562
runtime::latest_reader< T > latest_reader() const noexcept
Definition: sync.h:598
std::size_t size() const noexcept
Definition: sync.h:570
bool try_send_from_isr(T &&value)
Definition: sync.h:544
bool try_receive(T &value)
Definition: sync.h:549
bool latest(T &value) const
Definition: sync.h:579
T value_type
Definition: sync.h:513
receive_awaitable receive() noexcept
Definition: sync.h:681
bool full() const noexcept
Definition: sync.h:566
runtime::receiver< T > receiver() noexcept
Definition: sync.h:593
Definition: sync.h:473
latest_reader(Channel const &channel) noexcept
Definition: sync.h:478
bool latest(T &value) const
Definition: sync.h:486
Definition: sync.h:446
receiver(Channel &channel) noexcept
Definition: sync.h:451
bool try_receive(T &value) const
Definition: sync.h:459
Definition: sync.h:409
bool try_send(T const &value) const
Definition: sync.h:425
bool try_send(T &&value) const
Definition: sync.h:430
sender(Channel &channel) noexcept
Definition: sync.h:414
Definition: sync.h:363
T await_resume() const
Definition: sync.h:385
void await_suspend(task::handle_type handle) noexcept
Definition: sync.h:375
wait_awaitable(signal &source) noexcept
Definition: sync.h:365
bool await_ready() const noexcept
Definition: sync.h:370
void await_cancel(task_context &context) noexcept
Definition: sync.h:380
Definition: sync.h:327
T const & value() const noexcept
Definition: sync.h:357
void publish_from_isr(T &&value)
Definition: sync.h:348
bool ready() const noexcept
Definition: sync.h:353
void publish(T &&value)
Definition: sync.h:336
void publish_from_isr(T const &value)
Definition: sync.h:343
wait_awaitable wait() noexcept
Definition: sync.h:394
void publish(T const &value)
Definition: sync.h:329
Definition: coroutine.h:196
std::coroutine_handle< promise_type > handle_type
Definition: coroutine.h:46
Static coroutine tasks for tsm runtime executors.
::tsm::overflow::reject_newest reject_newest
Definition: concepts.h:87
Definition: bare_metal.h:20
Runtime policy tags for dispatch and bounded queue admission.
Definition: policy.h:40
Definition: policy.h:38