annotate Source/Utility/Scheduler.cpp @ 56:b4a2d2ae43cf tip

merge
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Fri, 23 Nov 2018 15:48:14 +0000
parents dfff66c07936
children
rev   line source
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 }