andrewm@0
|
1 /*
|
andrewm@0
|
2 TouchKeys: multi-touch musical keyboard control software
|
andrewm@0
|
3 Copyright (c) 2013 Andrew McPherson
|
andrewm@0
|
4
|
andrewm@0
|
5 This program is free software: you can redistribute it and/or modify
|
andrewm@0
|
6 it under the terms of the GNU General Public License as published by
|
andrewm@0
|
7 the Free Software Foundation, either version 3 of the License, or
|
andrewm@0
|
8 (at your option) any later version.
|
andrewm@0
|
9
|
andrewm@0
|
10 This program is distributed in the hope that it will be useful,
|
andrewm@0
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
andrewm@0
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
andrewm@0
|
13 GNU General Public License for more details.
|
andrewm@0
|
14
|
andrewm@0
|
15 You should have received a copy of the GNU General Public License
|
andrewm@0
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
andrewm@0
|
17
|
andrewm@0
|
18 =====================================================================
|
andrewm@0
|
19
|
andrewm@0
|
20 Scheduler.cpp: allows actions to be scheduled at future times. Runs a
|
andrewm@0
|
21 thread in which these actions are executed.
|
andrewm@0
|
22 */
|
andrewm@0
|
23
|
andrewm@0
|
24 #include "Scheduler.h"
|
andrewm@0
|
25 #undef DEBUG_SCHEDULER
|
andrewm@0
|
26
|
andrewm@0
|
27 using std::cout;
|
andrewm@0
|
28
|
andrewm@0
|
29 const timestamp_diff_type Scheduler::kAllowableAdvanceExecutionTime = milliseconds_to_timestamp(1.0);
|
andrewm@0
|
30
|
andrewm@0
|
31 // Start the thread handling the scheduling. Pass it an initial timestamp.
|
andrewm@0
|
32 void Scheduler::start(timestamp_type where) {
|
andrewm@0
|
33 if(isRunning_)
|
andrewm@0
|
34 return;
|
andrewm@0
|
35 startingTimestamp_ = where;
|
andrewm@0
|
36 startThread();
|
andrewm@0
|
37 }
|
andrewm@0
|
38
|
andrewm@0
|
39 // Stop the scheduler thread if it is currently running. Events will remain
|
andrewm@0
|
40 // in the queue unless explicitly cleared.
|
andrewm@0
|
41 void Scheduler::stop() {
|
andrewm@0
|
42 if(!isRunning_)
|
andrewm@0
|
43 return;
|
andrewm@0
|
44
|
andrewm@0
|
45 // Tell the thread to quit and signal the event it waits on
|
andrewm@0
|
46 signalThreadShouldExit();
|
andrewm@0
|
47 waitableEvent_.signal();
|
andrewm@0
|
48 stopThread(-1);
|
andrewm@0
|
49
|
andrewm@0
|
50 isRunning_ = false;
|
andrewm@0
|
51 }
|
andrewm@0
|
52
|
andrewm@0
|
53 // Return the current timestamp, relative to this class's start time.
|
andrewm@0
|
54 timestamp_type Scheduler::currentTimestamp() {
|
andrewm@0
|
55 if(!isRunning_)
|
andrewm@0
|
56 return 0;
|
andrewm@0
|
57 return milliseconds_to_timestamp(Time::getMillisecondCounterHiRes() - startTimeMilliseconds_);
|
andrewm@0
|
58 //return ptime_to_timestamp(microsec_clock::universal_time() - startTime_);
|
andrewm@0
|
59 }
|
andrewm@0
|
60
|
andrewm@0
|
61 // Schedule a new event
|
andrewm@0
|
62 void Scheduler::schedule(void *who, action func, timestamp_type timestamp) {
|
andrewm@0
|
63 ScopedLock sl(eventMutex_);
|
andrewm@0
|
64
|
andrewm@0
|
65 #ifdef DEBUG_SCHEDULER
|
andrewm@0
|
66 std::cerr << "Scheduler::schedule: " << who << ", " << timestamp << " (" << timestamp - currentTimestamp() << " from now)\n";
|
andrewm@0
|
67 #endif
|
andrewm@0
|
68
|
andrewm@0
|
69 // Check if this timestamp will become the next thing in the queue
|
andrewm@0
|
70 bool newActionWillComeFirst = false;
|
andrewm@0
|
71 if(events_.empty())
|
andrewm@0
|
72 newActionWillComeFirst = true;
|
andrewm@0
|
73 else if(timestamp < events_.begin()->first)
|
andrewm@0
|
74 newActionWillComeFirst = true;
|
andrewm@0
|
75 events_.insert(std::pair<timestamp_type,std::pair<void*, action> >
|
andrewm@0
|
76 (timestamp, std::pair<void*, action>(who, func)));
|
andrewm@0
|
77
|
andrewm@0
|
78 // Tell the thread to wake up and recheck its status if the
|
andrewm@0
|
79 // time of the next event has changed
|
andrewm@0
|
80 if(newActionWillComeFirst)
|
andrewm@0
|
81 waitableEvent_.signal();
|
andrewm@0
|
82 }
|
andrewm@0
|
83
|
andrewm@0
|
84 // Remove an existing event
|
andrewm@0
|
85 void Scheduler::unschedule(void *who, timestamp_type timestamp) {
|
andrewm@0
|
86 #ifdef DEBUG_SCHEDULER
|
andrewm@0
|
87 std::cerr << "Scheduler::unschedule: " << who << ", " << timestamp << std::endl;
|
andrewm@0
|
88 #endif
|
andrewm@0
|
89 ScopedLock sl(eventMutex_);
|
andrewm@0
|
90
|
andrewm@0
|
91 // Find all events with this timestamp, and remove only the ones matching the given source
|
andrewm@0
|
92 std::multimap<timestamp_type, std::pair<void*, action> >::iterator it;
|
andrewm@0
|
93
|
andrewm@0
|
94 if(timestamp == 0) {
|
andrewm@0
|
95 // Remove all events from this source
|
andrewm@0
|
96 it = events_.begin();
|
andrewm@0
|
97 while(it != events_.end()) {
|
andrewm@0
|
98 #ifdef DEBUG_SCHEDULER
|
andrewm@0
|
99 std::cerr << "| (" << it->first << ", " << it->second.first << ")\n";
|
andrewm@0
|
100 #endif
|
andrewm@0
|
101 if(it->second.first == who) {
|
andrewm@0
|
102 #ifdef DEBUG_SCHEDULER
|
andrewm@0
|
103 std::cerr << "--> erased " << it->first << ", " << it->second.first << ")\n";
|
andrewm@0
|
104 #endif
|
andrewm@0
|
105 events_.erase(it++);
|
andrewm@0
|
106 }
|
andrewm@0
|
107 else
|
andrewm@0
|
108 it++;
|
andrewm@0
|
109 }
|
andrewm@0
|
110 }
|
andrewm@0
|
111 else {
|
andrewm@0
|
112 // Remove only a specific event from this source with the given timestmap
|
andrewm@0
|
113 it = events_.find(timestamp);
|
andrewm@0
|
114 while(it != events_.end()) {
|
andrewm@0
|
115 if(it->second.first == who) {
|
andrewm@0
|
116 #ifdef DEBUG_SCHEDULER
|
andrewm@0
|
117 std::cerr << "--> erased " << it->first << ", " << it->second.first << ")\n";
|
andrewm@0
|
118 #endif
|
andrewm@0
|
119 events_.erase(it++);
|
andrewm@0
|
120 }
|
andrewm@0
|
121 else
|
andrewm@0
|
122 it++;
|
andrewm@0
|
123 }
|
andrewm@0
|
124 }
|
andrewm@0
|
125
|
andrewm@0
|
126 #ifdef DEBUG_SCHEDULER
|
andrewm@0
|
127 std::cerr << "Scheduler::unschedule: done\n";
|
andrewm@0
|
128 #endif
|
andrewm@0
|
129 // No need to wake up the thread...
|
andrewm@0
|
130 }
|
andrewm@0
|
131
|
andrewm@0
|
132 // Clear all events from the queue
|
andrewm@0
|
133 void Scheduler::clear() {
|
andrewm@0
|
134 ScopedLock sl(eventMutex_);
|
andrewm@0
|
135
|
andrewm@0
|
136 events_.clear();
|
andrewm@0
|
137
|
andrewm@0
|
138 // No need to signal the condition variable. If the thread is waiting, it can keep waiting.
|
andrewm@0
|
139 }
|
andrewm@0
|
140
|
andrewm@0
|
141 // This function runs in its own thread (from the Juce parent class). It looks for the next event
|
andrewm@0
|
142 // in the queue. When its time arrives, the event is executed and removed from the queue.
|
andrewm@0
|
143 // When the queue is empty, or the next event has not arrived yet, the thread sleeps.
|
andrewm@0
|
144
|
andrewm@0
|
145 void Scheduler::run() {
|
andrewm@0
|
146 // Start with the mutex locked. The wait() methods will unlock it.
|
andrewm@0
|
147 eventMutex_.enter();
|
andrewm@0
|
148
|
andrewm@0
|
149 // Find the start time, against which our offsets will be measured.
|
andrewm@0
|
150 //startTime_ = microsec_clock::universal_time();
|
andrewm@0
|
151 startTimeMilliseconds_ = Time::getMillisecondCounterHiRes();
|
andrewm@0
|
152 isRunning_ = true;
|
andrewm@0
|
153
|
andrewm@0
|
154 // This will run until the thread is interrupted (in the stop() method)
|
andrewm@0
|
155 // events_ is ordered by increasing timestamp, so the next event to execute is always the first item.
|
andrewm@0
|
156 while(!threadShouldExit()) {
|
andrewm@0
|
157 if(events_.empty()) { // If there are no events in the queue, wait until we're signaled
|
andrewm@0
|
158 eventMutex_.exit(); // that a new one comes in. Unlock the mutex and wait.
|
andrewm@0
|
159 waitableEvent_.wait();
|
andrewm@0
|
160 eventMutex_.enter();
|
andrewm@0
|
161 }
|
andrewm@0
|
162 else {
|
andrewm@0
|
163 timestamp_type t = events_.begin()->first; // Find the timestamp of the first event
|
andrewm@0
|
164 double targetTimeMilliseconds = startTimeMilliseconds_ + timestamp_to_milliseconds(t);
|
andrewm@0
|
165
|
andrewm@0
|
166 // Wait until that time arrives, provided it hasn't already
|
andrewm@20
|
167 int timeDifferenceMilliseconds = (int)floor(targetTimeMilliseconds - Time::getMillisecondCounterHiRes() + 0.5);
|
andrewm@0
|
168 #ifdef DEBUG_SCHEDULER
|
andrewm@0
|
169 std::cerr << "Scheduler::run: waiting for " << timeDifferenceMilliseconds << "ms\n";
|
andrewm@0
|
170 #endif
|
andrewm@0
|
171 if(timeDifferenceMilliseconds > 0) {
|
andrewm@0
|
172 eventMutex_.exit();
|
andrewm@0
|
173 waitableEvent_.wait(timeDifferenceMilliseconds);
|
andrewm@0
|
174 eventMutex_.enter();
|
andrewm@0
|
175 }
|
andrewm@0
|
176 }
|
andrewm@0
|
177
|
andrewm@0
|
178 waitableEvent_.reset(); // Clear the signal
|
andrewm@0
|
179
|
andrewm@0
|
180 if(threadShouldExit())
|
andrewm@0
|
181 break;
|
andrewm@0
|
182
|
andrewm@0
|
183 // At this point, the mutex is locked. We can change the contents of events_ without worrying about disrupting anything.
|
andrewm@0
|
184
|
andrewm@0
|
185 if(events_.empty()) // Double check that we actually have an event to execute
|
andrewm@0
|
186 continue;
|
andrewm@0
|
187 if(currentTimestamp() + kAllowableAdvanceExecutionTime < events_.begin()->first) {
|
andrewm@0
|
188 #ifdef DEBUG_SCHEDULER
|
andrewm@0
|
189 std::cerr << "Scheduler::run: next event hasn't arrived (currently " << currentTimestamp() << ", waiting for " << events_.begin()->first << "\n";
|
andrewm@0
|
190 #endif
|
andrewm@0
|
191 continue;
|
andrewm@0
|
192 }
|
andrewm@0
|
193
|
andrewm@0
|
194 // Run the function that's stored, which takes no arguments and returns a timestamp
|
andrewm@0
|
195 // of the next time this particular function should run.
|
andrewm@0
|
196 std::multimap<timestamp_type, std::pair<void*, action> >::iterator it = events_.begin();
|
andrewm@0
|
197 action actionFunction = (it->second).second;
|
andrewm@0
|
198 //timestamp_type testingTimestamp = it->first;
|
andrewm@0
|
199 void *who = it->second.first;
|
andrewm@0
|
200
|
andrewm@0
|
201 #ifdef DEBUG_SCHEDULER
|
andrewm@0
|
202 std::cerr << "Scheduler::run: " << who << ", " << it->first << std::endl;
|
andrewm@0
|
203 #endif
|
andrewm@0
|
204
|
andrewm@0
|
205 timestamp_type timeOfNextEvent = actionFunction();
|
andrewm@0
|
206
|
andrewm@0
|
207 // Remove the last event from the queue
|
andrewm@0
|
208 events_.erase(it);
|
andrewm@0
|
209
|
andrewm@0
|
210 if(timeOfNextEvent > 0) {
|
andrewm@0
|
211 // Reschedule the same event for some (hopefully) future time.
|
andrewm@0
|
212 events_.insert(std::pair<timestamp_type,std::pair<void*, action> >
|
andrewm@0
|
213 (timeOfNextEvent,
|
andrewm@0
|
214 std::pair<void*, action>(who, actionFunction)));
|
andrewm@0
|
215 }
|
andrewm@0
|
216 }
|
andrewm@0
|
217
|
andrewm@0
|
218 eventMutex_.exit();
|
andrewm@0
|
219 } |