summaryrefslogtreecommitdiff
path: root/src/tangara/events/event_queue.hpp
blob: aa7f472d55057e708a079b9e32f70302c60cbd1f (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
96
97
98
99
100
101
102
103
/*
 * Copyright 2023 jacqueline <me@jacqueline.id.au>
 *
 * SPDX-License-Identifier: GPL-3.0-only
 */

#pragma once

#include <functional>
#include <mutex>
#include <queue>
#include <type_traits>

#include "audio/audio_fsm.hpp"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "freertos/queue.h"
#include "system_fsm/system_fsm.hpp"
#include "tinyfsm.hpp"

#include "ui/ui_fsm.hpp"

namespace events {

class Queue {
 public:
  Queue() : has_events_(xSemaphoreCreateBinary()), mut_(), events_() {}

  auto Add(std::function<void(void)> fn) {
    {
      std::lock_guard<std::mutex> lock{mut_};
      events_.push_back(fn);
    }
    xSemaphoreGive(has_events_);
  }

  auto Service(TickType_t max_wait) -> bool {
    bool res = xSemaphoreTake(has_events_, max_wait);
    if (!res) {
      return false;
    }

    bool had_work = false;
    for (;;) {
      std::function<void(void)> fn;
      {
        std::lock_guard<std::mutex> lock{mut_};
        if (events_.empty()) {
          return had_work;
        }
        had_work = true;
        fn = events_.front();
        events_.pop_front();
      }
      std::invoke(fn);
    }
  }

  auto has_events() -> SemaphoreHandle_t { return has_events_; }

  Queue(Queue const&) = delete;
  void operator=(Queue const&) = delete;

 private:
  SemaphoreHandle_t has_events_;
  std::mutex mut_;
  std::list<std::function<void(void)>> events_;
};

template <class Machine>
class Dispatcher {
 public:
  Dispatcher(Queue* queue) : queue_(queue) {}

  template <typename Event>
  auto Dispatch(const Event& ev) -> void {
    auto dispatch_fn = [=]() {
      tinyfsm::FsmList<Machine>::template dispatch<Event>(ev);
    };
    queue_->Add(dispatch_fn);
  }

  auto RunOnTask(const std::function<void(void)>& fn) -> void {
    queue_->Add(fn);
  }

  Dispatcher(Dispatcher const&) = delete;
  void operator=(Dispatcher const&) = delete;

 private:
  Queue* queue_;
};

namespace queues {
auto SystemAndAudio() -> Queue*;
auto Ui() -> Queue*;
}  // namespace queues

auto System() -> Dispatcher<system_fsm::SystemState>&;
auto Audio() -> Dispatcher<audio::AudioState>&;
auto Ui() -> Dispatcher<ui::UiState>&;

}  // namespace events