Mercurial > hg > touchkeys
view Source/Utility/Scheduler.cpp @ 53:ff5d65c69e73
Updates to control passthrough, log playback, firmware update ability
author | Andrew McPherson <andrewm@eecs.qmul.ac.uk> |
---|---|
date | Mon, 02 Jan 2017 22:29:39 +0000 |
parents | dfff66c07936 |
children |
line wrap: on
line source
/* TouchKeys: multi-touch musical keyboard control software Copyright (c) 2013 Andrew McPherson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ===================================================================== Scheduler.cpp: allows actions to be scheduled at future times. Runs a thread in which these actions are executed. */ #include "Scheduler.h" #undef DEBUG_SCHEDULER using std::cout; const timestamp_diff_type Scheduler::kAllowableAdvanceExecutionTime = milliseconds_to_timestamp(1.0); // Start the thread handling the scheduling. Pass it an initial timestamp. void Scheduler::start(timestamp_type where) { if(isRunning_) return; startingTimestamp_ = where; startThread(); } // Stop the scheduler thread if it is currently running. Events will remain // in the queue unless explicitly cleared. void Scheduler::stop() { if(!isRunning_) return; // Tell the thread to quit and signal the event it waits on signalThreadShouldExit(); waitableEvent_.signal(); stopThread(-1); isRunning_ = false; } // Return the current timestamp, relative to this class's start time. timestamp_type Scheduler::currentTimestamp() { if(!isRunning_) return 0; return milliseconds_to_timestamp(Time::getMillisecondCounterHiRes() - startTimeMilliseconds_); //return ptime_to_timestamp(microsec_clock::universal_time() - startTime_); } // Schedule a new event void Scheduler::schedule(void *who, action func, timestamp_type timestamp) { ScopedLock sl(eventMutex_); #ifdef DEBUG_SCHEDULER std::cerr << "Scheduler::schedule: " << who << ", " << timestamp << " (" << timestamp - currentTimestamp() << " from now)\n"; #endif // Check if this timestamp will become the next thing in the queue bool newActionWillComeFirst = false; if(events_.empty()) newActionWillComeFirst = true; else if(timestamp < events_.begin()->first) newActionWillComeFirst = true; events_.insert(std::pair<timestamp_type,std::pair<void*, action> > (timestamp, std::pair<void*, action>(who, func))); // Tell the thread to wake up and recheck its status if the // time of the next event has changed if(newActionWillComeFirst) waitableEvent_.signal(); } // Remove an existing event void Scheduler::unschedule(void *who, timestamp_type timestamp) { #ifdef DEBUG_SCHEDULER std::cerr << "Scheduler::unschedule: " << who << ", " << timestamp << std::endl; #endif ScopedLock sl(eventMutex_); // Find all events with this timestamp, and remove only the ones matching the given source std::multimap<timestamp_type, std::pair<void*, action> >::iterator it; if(timestamp == 0) { // Remove all events from this source it = events_.begin(); while(it != events_.end()) { #ifdef DEBUG_SCHEDULER std::cerr << "| (" << it->first << ", " << it->second.first << ")\n"; #endif if(it->second.first == who) { #ifdef DEBUG_SCHEDULER std::cerr << "--> erased " << it->first << ", " << it->second.first << ")\n"; #endif events_.erase(it++); } else it++; } } else { // Remove only a specific event from this source with the given timestmap it = events_.find(timestamp); while(it != events_.end()) { if(it->second.first == who) { #ifdef DEBUG_SCHEDULER std::cerr << "--> erased " << it->first << ", " << it->second.first << ")\n"; #endif events_.erase(it++); } else it++; } } #ifdef DEBUG_SCHEDULER std::cerr << "Scheduler::unschedule: done\n"; #endif // No need to wake up the thread... } // Clear all events from the queue void Scheduler::clear() { ScopedLock sl(eventMutex_); events_.clear(); // No need to signal the condition variable. If the thread is waiting, it can keep waiting. } // This function runs in its own thread (from the Juce parent class). It looks for the next event // in the queue. When its time arrives, the event is executed and removed from the queue. // When the queue is empty, or the next event has not arrived yet, the thread sleeps. void Scheduler::run() { // Start with the mutex locked. The wait() methods will unlock it. eventMutex_.enter(); // Find the start time, against which our offsets will be measured. //startTime_ = microsec_clock::universal_time(); startTimeMilliseconds_ = Time::getMillisecondCounterHiRes(); isRunning_ = true; // This will run until the thread is interrupted (in the stop() method) // events_ is ordered by increasing timestamp, so the next event to execute is always the first item. while(!threadShouldExit()) { if(events_.empty()) { // If there are no events in the queue, wait until we're signaled eventMutex_.exit(); // that a new one comes in. Unlock the mutex and wait. waitableEvent_.wait(); eventMutex_.enter(); } else { timestamp_type t = events_.begin()->first; // Find the timestamp of the first event double targetTimeMilliseconds = startTimeMilliseconds_ + timestamp_to_milliseconds(t); // Wait until that time arrives, provided it hasn't already int timeDifferenceMilliseconds = (int)floor(targetTimeMilliseconds - Time::getMillisecondCounterHiRes() + 0.5); #ifdef DEBUG_SCHEDULER std::cerr << "Scheduler::run: waiting for " << timeDifferenceMilliseconds << "ms\n"; #endif if(timeDifferenceMilliseconds > 0) { eventMutex_.exit(); waitableEvent_.wait(timeDifferenceMilliseconds); eventMutex_.enter(); } } waitableEvent_.reset(); // Clear the signal if(threadShouldExit()) break; // At this point, the mutex is locked. We can change the contents of events_ without worrying about disrupting anything. if(events_.empty()) // Double check that we actually have an event to execute continue; if(currentTimestamp() + kAllowableAdvanceExecutionTime < events_.begin()->first) { #ifdef DEBUG_SCHEDULER std::cerr << "Scheduler::run: next event hasn't arrived (currently " << currentTimestamp() << ", waiting for " << events_.begin()->first << "\n"; #endif continue; } // Run the function that's stored, which takes no arguments and returns a timestamp // of the next time this particular function should run. std::multimap<timestamp_type, std::pair<void*, action> >::iterator it = events_.begin(); action actionFunction = (it->second).second; //timestamp_type testingTimestamp = it->first; void *who = it->second.first; #ifdef DEBUG_SCHEDULER std::cerr << "Scheduler::run: " << who << ", " << it->first << std::endl; #endif timestamp_type timeOfNextEvent = actionFunction(); // Remove the last event from the queue events_.erase(it); if(timeOfNextEvent > 0) { // Reschedule the same event for some (hopefully) future time. events_.insert(std::pair<timestamp_type,std::pair<void*, action> > (timeOfNextEvent, std::pair<void*, action>(who, actionFunction))); } } eventMutex_.exit(); }