jubilant-funicular
CallbackManager.cpp
1 #include "nta/CallbackManager.h"
2 #include "nta/Logger.h"
3 
4 namespace nta {
5  std::map<uint64_t, CallbackManager::event> CallbackManager::m_active;
6  std::map<uint64_t, std::vector<CallbackManager::event*>> CallbackManager::m_queue;
7  utils::ThreadPool CallbackManager::m_pool(8);
9  std::atomic<uint64_t> CallbackManager::m_next(0);
10  std::mutex CallbackManager::m_mutex;
11  std::condition_variable_any CallbackManager::m_cv;
12  bool CallbackManager::m_working(true);
14 
15  uint64_t CallbackManager::setInterval(const Thunk& thunk, uint64_t when, uint64_t period) {
16  std::lock_guard<std::mutex> lg(m_mutex);
17 
18  event& e = m_active[m_next];
19  e.id = m_next++;
20  e.when = m_curr_frame + when;
21  e.period = period;
22  e.thunk = thunk;
23 
24  bool empty = m_queue.empty();
25  m_queue[e.when].push_back(&e);
26  // Second half of || is impossible, but the analagous check
27  // won't be impossible if we base time on an actual clock
28  if (empty || e.when < m_curr_frame) {
29  m_cv.notify_one();
30  }
31  return e.id;
32  }
33  uint64_t CallbackManager::setTimeout(const Thunk& thunk, uint64_t when) {
34  return setInterval(thunk, when, 0);
35  }
36  bool CallbackManager::delay(uint64_t id, uint64_t when, bool absolute) {
37  std::lock_guard<std::mutex> lg(m_mutex);
38  if (m_active.find(id) == m_active.end()) return false;
39 
40  m_mutex.unlock();
41  dequeue(id);
42  m_mutex.lock();
43 
44  auto& e = m_active[id];
45  e.when = absolute ? when + m_curr_frame : when + e.when;
46  m_queue[e.when].push_back(&e);
47  return true;
48  }
50  Logger::writeToLog("Initializing CallbackManager...");
51  m_dispatcher = std::thread([&]{dispatch();});
52  m_next = m_curr_frame = 0;
53  m_working = true;
54  Logger::writeToLog("Initialized CallbackManager");
55  }
57  while (true) {
58  m_mutex.lock();
59  m_cv.wait(m_mutex, [&](){return !m_working ||
60  (!m_queue.empty() && m_curr_frame >= m_queue.begin()->first);});
61  if (!m_working) break;
62  m_mutex.unlock();
63 
64  while (true) {
65  m_mutex.lock();
66  if (m_queue.empty() || m_curr_frame < m_queue.begin()->first) {
67  m_mutex.unlock();
68  break;
69  }
70 
71  auto& es = m_queue.begin()->second;
72  while (!es.empty()) {
73  event* e = es[0];
74  m_pool.schedule(e->thunk);
75 
76  m_mutex.unlock();
77  e->period > 0 ? (void)delay(e->id, e->period, false) : clear(e->id);
78  m_mutex.lock();
79  }
80  m_mutex.unlock();
81  }
82  }
83  m_mutex.unlock();
84  }
85  void CallbackManager::dequeue(uint64_t id) {
86  std::lock_guard<std::mutex> lg(m_mutex);
87 
88  event& e = m_active[id];
89  auto& es = m_queue[e.when];
90  for (size_t i = 0; i < es.size(); i++) {
91  if (es[i] == &e) {
92  es.erase(es.begin() + i);
93  break;
94  }
95  }
96  if (es.empty()) m_queue.erase(e.when);
97  }
98  void CallbackManager::clear(uint64_t id) {
99  std::lock_guard<std::mutex> lg(m_mutex);
100  if (m_active.find(id) == m_active.end()) return;
101 
102  m_mutex.unlock();
103  dequeue(id);
104  m_mutex.lock();
105 
106  m_active.erase(id);
107  }
109  Logger::writeToLog("Destroying CallbackManager...");
110  m_mutex.lock();
111  m_working = false;
112  m_cv.notify_one();
113  m_mutex.unlock();
114 
115  m_pool.wait();
116  m_dispatcher.join();
117 
118  m_curr_frame = 0;
119  m_queue.clear();
120  m_active.clear();
121  Logger::writeToLog("Destroyed CallbackManager");
122  }
124  std::lock_guard<std::mutex> lg(m_mutex);
125  m_curr_frame++;
126  if (!m_queue.empty() && m_queue.begin()->first < m_curr_frame) m_cv.notify_one();
127  }
128 }
nta::Logger::writeToLog
static void writeToLog(crstring entry)
writes an entry in the log
Definition: Logger.cpp:17
nta::CallbackManager::increment_frame
static void increment_frame()
Informs CallbackManager that a frame has passed.
Definition: CallbackManager.cpp:123
nta::CallbackManager::setTimeout
static uint64_t setTimeout(const Thunk &thunk, uint64_t when)
Schedules a function to be called once after when frames and then repeatedly after.
Definition: CallbackManager.cpp:33
nta::CallbackManager::m_next
static std::atomic< uint64_t > m_next
id given to the next created event
Definition: CallbackManager.h:42
nta::CallbackManager::m_active
static std::map< uint64_t, event > m_active
The events waiting to happen (key is event.id)
Definition: CallbackManager.h:33
nta::utils::ThreadPool::wait
void wait()
Waits for all scheduled functions to finish execution.
Definition: ThreadPool.cpp:68
nta::CallbackManager::m_queue
static std::map< uint64_t, std::vector< event * > > m_queue
"Priority queue" of events ordered by when they should occur
Definition: CallbackManager.h:35
nta::CallbackManager::clear
static void clear(uint64_t id)
Removes the event with the given id.
Definition: CallbackManager.cpp:98
nta
Definition: Animation2D.h:6
nta::CallbackManager::delay
static bool delay(uint64_t id, uint64_t when, bool absolute=true)
Changes when an event is next scheduled to run.
Definition: CallbackManager.cpp:36
nta::CallbackManager::dequeue
static void dequeue(uint64_t id)
Removes event from m_queue but not m_active (assumes id is valid)
Definition: CallbackManager.cpp:85
nta::CallbackManager::m_pool
static utils::ThreadPool m_pool
ThreadPool used for scheduling events to happen.
Definition: CallbackManager.h:38
nta::CallbackManager::init
static void init()
Initializes the CallbackManager.
Definition: CallbackManager.cpp:49
nta::utils::ThreadPool::schedule
void schedule(const Thunk &thunk)
Schedules a function to be executed.
Definition: ThreadPool.cpp:62
nta::CallbackManager::m_curr_frame
static uint64_t m_curr_frame
The current frame.
Definition: CallbackManager.h:49
nta::CallbackManager::m_dispatcher
static std::thread m_dispatcher
Thread used to dispatch events.
Definition: CallbackManager.h:40
nta::CallbackManager::dispatch
static void dispatch()
Definition: CallbackManager.cpp:56
nta::CallbackManager::destroy
static void destroy()
Destroys the CallbackManager.
Definition: CallbackManager.cpp:108
nta::CallbackManager::setInterval
static uint64_t setInterval(const Thunk &thunk, uint64_t when, uint64_t period)
Definition: CallbackManager.cpp:15
nta::CallbackManager::m_working
static bool m_working
Global flag saying whether or not the CallbackManager has been destroyed.
Definition: CallbackManager.h:47