diff --git a/src/OSLikeStuff/scheduler_api.h b/src/OSLikeStuff/scheduler_api.h index 7df4e935dc..9c81e98fa3 100644 --- a/src/OSLikeStuff/scheduler_api.h +++ b/src/OSLikeStuff/scheduler_api.h @@ -29,6 +29,13 @@ extern "C" { typedef void (*TaskHandle)(); typedef bool (*RunCondition)(); typedef int8_t TaskID; +typedef uint32_t ResourceID; +#define RESOURCE_NONE 0 +// for actually reading the SD +#define RESOURCE_SD 1 +#define RESOURCE_USB 2 +// for things that can't run in the SD routine +#define RESOURCE_SD_ROUTINE 4 /// Schedule a task that will be called at a regular interval. /// @@ -44,15 +51,17 @@ typedef int8_t TaskID; /// @param priority Priority of the task. Tasks with lower numbers are given preference over tasks with higher numbers. /// @param backOffTime Minimum time from completing the task to calling it again in seconds. /// @param targetTimeBetweenCalls Desired time between calls to the task, including the runtime for the task itself. +/// @param resource uint8_t addRepeatingTask(TaskHandle task, uint8_t priority, double backOffTime, double targetTimeBetweenCalls, - double maxTimeBetweenCalls, const char* name); + double maxTimeBetweenCalls, const char* name, ResourceID resource); /// Add a task to run once, aiming to run at current time + timeToWait and worst case run at timeToWait*10 -uint8_t addOnceTask(TaskHandle task, uint8_t priority, double timeToWait, const char* name); +uint8_t addOnceTask(TaskHandle task, uint8_t priority, double timeToWait, const char* name, ResourceID resources); /// add a task that runs only after the condition returns true. Condition checks should be very fast or they could /// interfere with scheduling -uint8_t addConditionalTask(TaskHandle task, uint8_t priority, RunCondition condition, const char* name); +uint8_t addConditionalTask(TaskHandle task, uint8_t priority, RunCondition condition, const char* name, + ResourceID resources); void ignoreForStats(); double getAverageRunTimeforCurrentTask(); double getSystemTime(); diff --git a/src/OSLikeStuff/task_scheduler/resource_checker.h b/src/OSLikeStuff/task_scheduler/resource_checker.h new file mode 100644 index 0000000000..d3443a39bb --- /dev/null +++ b/src/OSLikeStuff/task_scheduler/resource_checker.h @@ -0,0 +1,51 @@ +/* + * Copyright © 2025 Mark Adams + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware 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 . + */ +#ifndef RESOURCE_CHECKER_H +#define RESOURCE_CHECKER_H +#include +#include +#include +extern uint8_t currentlyAccessingCard; +extern uint32_t usbLock; +// this is basically a bitset however the enums need to be exposed to C code and this is easier to keep synced +class ResourceChecker { + uint32_t resources_{0}; + +public: + ResourceChecker() = default; + ResourceChecker(ResourceID resources) : resources_(resources) {}; + /// returns whether all resources are available. This is basically shitty priority ceiling to avoid the potential of + /// a task locking one resource and then trying to yield while it waits for another + bool checkResources() const { + if (resources_ == RESOURCE_NONE) { + return true; + } + bool anythingLocked = false; + if ((resources_ & RESOURCE_SD) != 0u) { + anythingLocked |= currentlyAccessingCard; + } + if ((resources_ & RESOURCE_USB) != 0u) { + anythingLocked |= usbLock; + } + if ((resources_ & RESOURCE_SD_ROUTINE) != 0u) { + anythingLocked |= sdRoutineLock; + } + return !anythingLocked; + } +}; + +#endif // RESOURCE_CHECKER_H diff --git a/src/OSLikeStuff/task_scheduler/task.h b/src/OSLikeStuff/task_scheduler/task.h index 6aa11f40be..765f133325 100644 --- a/src/OSLikeStuff/task_scheduler/task.h +++ b/src/OSLikeStuff/task_scheduler/task.h @@ -18,6 +18,7 @@ #ifndef DELUGE_TASK_H #define DELUGE_TASK_H #include "OSLikeStuff/scheduler_api.h" +#include "resource_checker.h" // internal to the scheduler - do not include from anywhere else struct StatBlock { @@ -64,16 +65,19 @@ struct Task { Task() = default; // constructor for making a "once" task - Task(TaskHandle _handle, uint8_t _priority, Time timeNow, Time _timeToWait, const char* _name) - : handle(_handle), lastCallTime(timeNow), removeAfterUse(true), name(_name) { + Task(TaskHandle _handle, uint8_t _priority, Time timeNow, Time _timeToWait, const char* _name, + ResourceChecker checker) + : handle(_handle), lastCallTime(timeNow), removeAfterUse(true), name(_name), _checker(checker) { schedule = TaskSchedule{_priority, _timeToWait, _timeToWait, _timeToWait * 2}; } // makes a repeating task - Task(TaskHandle task, TaskSchedule _schedule, const char* _name) : handle(task), schedule(_schedule), name(_name) {} + Task(TaskHandle task, TaskSchedule _schedule, const char* _name, ResourceChecker checker) + : handle(task), schedule(_schedule), name(_name), _checker(checker) {} // makes a conditional once task - Task(TaskHandle task, uint8_t priority, RunCondition _condition, const char* _name) - : handle(task), state(State::BLOCKED), condition(_condition), removeAfterUse(true), name(_name) { + Task(TaskHandle task, uint8_t priority, RunCondition _condition, const char* _name, ResourceChecker checker) + : handle(task), state(State::BLOCKED), condition(_condition), removeAfterUse(true), name(_name), + _checker(checker) { // good to go as soon as it's marked as runnable schedule = {priority, 0, 0, 0}; @@ -102,11 +106,14 @@ struct Task { return false; } - [[nodiscard]] bool isReady(Time currentTime) const { return state == State::READY && isReleased(currentTime); }; - [[nodiscard]] bool isRunnable() const { return state == State::READY; } + [[nodiscard]] bool isReady(Time currentTime) const { + return state == State::READY && isReleased(currentTime) && resourcesAvailable(); + }; + [[nodiscard]] bool isRunnable() const { return state == State::READY && resourcesAvailable(); } [[nodiscard]] bool isReleased(Time currentTime) const { return currentTime - lastFinishTime > schedule.backOffPeriod; } + [[nodiscard]] bool resourcesAvailable() const { return _checker.checkResources(); } TaskHandle handle{nullptr}; TaskSchedule schedule{0, 0, 0, 0}; Time idealCallTime{0}; @@ -126,6 +133,8 @@ struct Task { Time totalTime{0}; int32_t timesCalled{0}; Time lastRunTime; + + ResourceChecker _checker; }; #endif // DELUGE_TASK_H diff --git a/src/OSLikeStuff/task_scheduler/task_scheduler.cpp b/src/OSLikeStuff/task_scheduler/task_scheduler.cpp index 6a6dd228dc..f525973de6 100644 --- a/src/OSLikeStuff/task_scheduler/task_scheduler.cpp +++ b/src/OSLikeStuff/task_scheduler/task_scheduler.cpp @@ -18,8 +18,8 @@ #include "OSLikeStuff/task_scheduler/task_scheduler.h" #include "io/debug/log.h" +#include "resource_checker.h" #include -#include #if !IN_UNIT_TESTS #include "memory/general_memory_allocator.h" @@ -90,6 +90,9 @@ TaskID TaskManager::chooseBestTask(Time deadline) { // first look based on target time for (int i = (numActiveTasks - 1); i >= 0; i--) { struct Task* t = &list[sortedList[i].task]; + if (!t->isRunnable()) { + continue; + } struct TaskSchedule* s = &t->schedule; if (currentTime + t->durationStats.average < nextFinishTime && currentTime - t->lastFinishTime > s->targetInterval @@ -100,6 +103,9 @@ TaskID TaskManager::chooseBestTask(Time deadline) { // then look based on min time just to avoid busy waiting for (int i = (numActiveTasks - 1); i >= 0; i--) { struct Task* t = &list[sortedList[i].task]; + if (!t->isRunnable()) { + continue; + } struct TaskSchedule* s = &t->schedule; if (currentTime + t->durationStats.average < nextFinishTime && currentTime - t->lastFinishTime > s->backOffPeriod) { @@ -124,33 +130,36 @@ TaskID TaskManager::insertTaskToList(Task task) { return index; }; -TaskID TaskManager::addRepeatingTask(TaskHandle task, TaskSchedule schedule, const char* name) { +TaskID TaskManager::addRepeatingTask(TaskHandle task, TaskSchedule schedule, const char* name, + ResourceChecker resources) { if (numRegisteredTasks >= (kMaxTasks)) { return -1; } - TaskID index = insertTaskToList(Task{task, schedule, name}); + TaskID index = insertTaskToList(Task{task, schedule, name, resources}); createSortedList(); return index; } -TaskID TaskManager::addOnceTask(TaskHandle task, uint8_t priority, Time timeToWait, const char* name) { +TaskID TaskManager::addOnceTask(TaskHandle task, uint8_t priority, Time timeToWait, const char* name, + ResourceChecker resources) { if (numRegisteredTasks >= (kMaxTasks)) { return -1; } Time timeToStart = running ? getSecondsFromStart() : Time(0); - TaskID index = insertTaskToList(Task{task, priority, timeToStart, timeToWait, name}); + TaskID index = insertTaskToList(Task{task, priority, timeToStart, timeToWait, name, resources}); createSortedList(); return index; } -TaskID TaskManager::addConditionalTask(TaskHandle task, uint8_t priority, RunCondition condition, const char* name) { +TaskID TaskManager::addConditionalTask(TaskHandle task, uint8_t priority, RunCondition condition, const char* name, + ResourceChecker resources) { if (numRegisteredTasks >= (kMaxTasks)) { return -1; } - TaskID index = insertTaskToList(Task{task, priority, condition, name}); + TaskID index = insertTaskToList(Task{task, priority, condition, name, resources}); createSortedList(); return index; } diff --git a/src/OSLikeStuff/task_scheduler/task_scheduler.h b/src/OSLikeStuff/task_scheduler/task_scheduler.h index 68109f0892..0e6f5e1b31 100644 --- a/src/OSLikeStuff/task_scheduler/task_scheduler.h +++ b/src/OSLikeStuff/task_scheduler/task_scheduler.h @@ -25,10 +25,11 @@ struct TaskManager { void removeTask(TaskID id); void runTask(TaskID id); TaskID chooseBestTask(Time deadline); - TaskID addRepeatingTask(TaskHandle task, TaskSchedule schedule, const char* name); + TaskID addRepeatingTask(TaskHandle task, TaskSchedule schedule, const char* name, ResourceChecker resources); - TaskID addOnceTask(TaskHandle task, uint8_t priority, Time timeToWait, const char* name); - TaskID addConditionalTask(TaskHandle task, uint8_t priority, RunCondition condition, const char* name); + TaskID addOnceTask(TaskHandle task, uint8_t priority, Time timeToWait, const char* name, ResourceChecker resources); + TaskID addConditionalTask(TaskHandle task, uint8_t priority, RunCondition condition, const char* name, + ResourceChecker resources); void createSortedList(); TaskID insertTaskToList(Task task); diff --git a/src/OSLikeStuff/task_scheduler/task_scheduler_c_api.cpp b/src/OSLikeStuff/task_scheduler/task_scheduler_c_api.cpp index 12fbb0f583..51e97f2ce7 100644 --- a/src/OSLikeStuff/task_scheduler/task_scheduler_c_api.cpp +++ b/src/OSLikeStuff/task_scheduler/task_scheduler_c_api.cpp @@ -1,8 +1,23 @@ -// -// Created by Mark Adams on 2024-12-05. -// +/* + * Copyright © 2025 Mark Adams + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware 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 . + */ + #include "OSLikeStuff/scheduler_api.h" #include "OSLikeStuff/task_scheduler/task_scheduler.h" +#include "resource_checker.h" extern TaskManager taskManager; @@ -23,16 +38,18 @@ void setNextRunTimeforCurrentTask(double seconds) { } uint8_t addRepeatingTask(TaskHandle task, uint8_t priority, double backOffTime, double targetTimeBetweenCalls, - double maxTimeBetweenCalls, const char* name) { + double maxTimeBetweenCalls, const char* name, ResourceID resources) { return taskManager.addRepeatingTask( - task, TaskSchedule{priority, backOffTime, targetTimeBetweenCalls, maxTimeBetweenCalls}, name); + task, TaskSchedule{priority, backOffTime, targetTimeBetweenCalls, maxTimeBetweenCalls}, name, + ResourceChecker{resources}); } -uint8_t addOnceTask(TaskHandle task, uint8_t priority, double timeToWait, const char* name) { - return taskManager.addOnceTask(task, priority, timeToWait, name); +uint8_t addOnceTask(TaskHandle task, uint8_t priority, double timeToWait, const char* name, ResourceID resources) { + return taskManager.addOnceTask(task, priority, timeToWait, name, ResourceChecker{resources}); } -uint8_t addConditionalTask(TaskHandle task, uint8_t priority, RunCondition condition, const char* name) { - return taskManager.addConditionalTask(task, priority, condition, name); +uint8_t addConditionalTask(TaskHandle task, uint8_t priority, RunCondition condition, const char* name, + ResourceID resources) { + return taskManager.addConditionalTask(task, priority, condition, name, ResourceChecker{resources}); } void yield(RunCondition until) { diff --git a/src/deluge/deluge.cpp b/src/deluge/deluge.cpp index f8c6a68c63..530ec92e6f 100644 --- a/src/deluge/deluge.cpp +++ b/src/deluge/deluge.cpp @@ -577,47 +577,53 @@ void registerTasks() { // 0-9: High priority (10 for dyn tasks) uint8_t p = 0; - addRepeatingTask(&(AudioEngine::routine), p++, 0.00001, 16 / 44100., 24 / 44100., "audio routine"); + addRepeatingTask(&(AudioEngine::routine), p++, 0.00001, 16 / 44100., 24 / 44100., "audio routine", RESOURCE_NONE); // this one runs quickly and frequently to check for encoder changes - addRepeatingTask([]() { encoders::readEncoders(); }, p++, 0.0005, 0.001, 0.001, "read encoders"); + addRepeatingTask([]() { encoders::readEncoders(); }, p++, 0.0005, 0.001, 0.001, "read encoders", RESOURCE_NONE); // formerly part of audio routine, updates midi and clock - addRepeatingTask([]() { playbackHandler.routine(); }, p++, 2 / 44100., 16 / 44100, 32 / 44100., "playback routine"); + addRepeatingTask([]() { playbackHandler.routine(); }, p++, 2 / 44100., 16 / 44100, 32 / 44100., "playback routine", + RESOURCE_NONE); + addRepeatingTask([]() { playbackHandler.midiRoutine(); }, p++, 2 / 44100., 16 / 44100, 32 / 44100., + "playback routine", RESOURCE_SD | RESOURCE_USB); addRepeatingTask([]() { audioFileManager.loadAnyEnqueuedClusters(128, false); }, p++, 0.00001, 0.00001, 0.00002, - "load clusters"); + "load clusters", RESOURCE_NONE); // handles sd card recorders // named "slow" but isn't actually, it handles audio recording setup - addRepeatingTask(&AudioEngine::slowRoutine, p++, 0.001, 0.005, 0.05, "audio slow"); - addRepeatingTask(&(readButtonsAndPadsOnce), p++, 0.005, 0.005, 0.01, "buttons and pads"); + addRepeatingTask(&AudioEngine::slowRoutine, p++, 0.001, 0.005, 0.05, "audio slow", RESOURCE_NONE); + addRepeatingTask(&(readButtonsAndPadsOnce), p++, 0.005, 0.005, 0.01, "buttons and pads", RESOURCE_NONE); // 11-19: Medium priority (20 for dyn tasks) p = 11; - addRepeatingTask([]() { encoders::interpretEncoders(true); }, p++, 0.005, 0.005, 0.01, "interpret encoders fast"); + addRepeatingTask([]() { encoders::interpretEncoders(true); }, p++, 0.005, 0.005, 0.01, "interpret encoders fast", + RESOURCE_NONE); // 30 Hz update desired? - addRepeatingTask(&doAnyPendingUIRendering, p++, 0.01, 0.01, 0.03, "pending UI"); + addRepeatingTask(&doAnyPendingUIRendering, p++, 0.01, 0.01, 0.03, "pending UI", RESOURCE_NONE); // this one actually actions them - addRepeatingTask([]() { encoders::interpretEncoders(false); }, p++, 0.005, 0.005, 0.01, "interpret encoders slow"); + addRepeatingTask([]() { encoders::interpretEncoders(false); }, p++, 0.005, 0.005, 0.01, "interpret encoders slow", + RESOURCE_SD_ROUTINE); // Check for and handle queued SysEx traffic - addRepeatingTask([]() { smSysex::handleNextSysEx(); }, p++, 0.0002, 0.0002, 0.01, "Handle pending SysEx traffic."); + addRepeatingTask([]() { smSysex::handleNextSysEx(); }, p++, 0.0002, 0.0002, 0.01, "Handle pending SysEx traffic.", + RESOURCE_SD); // 21-29: Low priority (30 for dyn tasks) p = 21; // these ones are actually "slow" -> file manager just checks if an sd card has been inserted, audio recorder checks // if recordings are finished - addRepeatingTask([]() { audioFileManager.slowRoutine(); }, p++, 0.1, 0.1, 0.2, "audio file slow"); - addRepeatingTask([]() { audioRecorder.slowRoutine(); }, p++, 0.01, 0.1, 0.1, "audio recorder slow"); + addRepeatingTask([]() { audioFileManager.slowRoutine(); }, p++, 0.1, 0.1, 0.2, "audio file slow", RESOURCE_SD); + addRepeatingTask([]() { audioRecorder.slowRoutine(); }, p++, 0.01, 0.1, 0.1, "audio recorder slow", RESOURCE_NONE); // formerly part of cluster loading (why? no idea), actions undo/redo midi commands - addRepeatingTask([]() { playbackHandler.slowRoutine(); }, p++, 0.01, 0.1, 0.1, "playback routine"); + addRepeatingTask([]() { playbackHandler.slowRoutine(); }, p++, 0.01, 0.1, 0.1, "playback routine", RESOURCE_SD); // 31-39: Idle priority (40 for dyn tasks) p = 31; - addRepeatingTask(&(PIC::flush), p++, 0.001, 0.001, 0.02, "PIC flush"); + addRepeatingTask(&(PIC::flush), p++, 0.001, 0.001, 0.02, "PIC flush", RESOURCE_NONE); if (hid::display::have_oled_screen) { - addRepeatingTask(&(oledRoutine), p++, 0.01, 0.01, 0.02, "oled routine"); + addRepeatingTask(&(oledRoutine), p++, 0.01, 0.01, 0.02, "oled routine", RESOURCE_NONE); } // needs to be called very frequently, // handles animations and checks on the timers for any infrequent actions // long term this should probably be made into an idle task - addRepeatingTask([]() { uiTimerManager.routine(); }, p++, 0.0001, 0.0007, 0.01, "ui routine"); + addRepeatingTask([]() { uiTimerManager.routine(); }, p++, 0.0001, 0.0007, 0.01, "ui routine", RESOURCE_NONE); // addRepeatingTask([]() { AudioEngine::routineWithClusterLoading(true); }, 0, 1 / 44100., 16 / 44100., 32 / 44100., // true); addRepeatingTask(&(AudioEngine::routine), 0, 16 / 44100., 64 / 44100., true); @@ -868,7 +874,7 @@ extern "C" int32_t deluge_main(void) { midiFollow.readDefaultsFromFile(); PadLEDs::setBrightnessLevel(FlashStorage::defaultPadBrightness); setupBlankSong(); // we always need to do this - addConditionalTask(setupStartupSong, 100, isCardReady, "load startup song"); + addConditionalTask(setupStartupSong, 100, isCardReady, "load startup song", RESOURCE_SD | RESOURCE_SD_ROUTINE); #ifdef TEST_VECTOR NoteVector noteVector; diff --git a/src/deluge/playback/playback_handler.cpp b/src/deluge/playback/playback_handler.cpp index 03045f5096..7ebeecc5d4 100644 --- a/src/deluge/playback/playback_handler.cpp +++ b/src/deluge/playback/playback_handler.cpp @@ -120,9 +120,7 @@ extern "C" uint32_t triggerClockRisingEdgeTimes[]; extern "C" uint32_t triggerClockRisingEdgesReceived; extern "C" uint32_t triggerClockRisingEdgesProcessed; -// This function will be called repeatedly, at all times, to see if it's time to do a tick, and such -void PlaybackHandler::routine() { - +void PlaybackHandler::midiRoutine() { // Check incoming USB MIDI midiEngine.checkIncomingUsbMidi(); @@ -130,6 +128,10 @@ void PlaybackHandler::routine() { for (int32_t i = 0; i < 12 && midiEngine.checkIncomingSerialMidi(); i++) { ; } +} + +// This function will be called repeatedly, at all times, to see if it's time to do a tick, and such +void PlaybackHandler::routine() { // Check analog clock input if (triggerClockRisingEdgesProcessed != triggerClockRisingEdgesReceived) { diff --git a/src/deluge/playback/playback_handler.h b/src/deluge/playback/playback_handler.h index 8ae9c6dcf9..0d7edae373 100644 --- a/src/deluge/playback/playback_handler.h +++ b/src/deluge/playback/playback_handler.h @@ -54,6 +54,7 @@ constexpr uint16_t metronomeValueBoundaries[16] = { class PlaybackHandler { public: PlaybackHandler(); + void midiRoutine(); void routine(); void playButtonPressed(int32_t buttonPressLatency); diff --git a/tests/unit/scheduler_tests.cpp b/tests/unit/scheduler_tests.cpp index bbbe88f92e..26b626bd2b 100644 --- a/tests/unit/scheduler_tests.cpp +++ b/tests/unit/scheduler_tests.cpp @@ -13,6 +13,9 @@ #include #endif +uint8_t currentlyAccessingCard = false; +uint32_t usbLock = false; +bool sdRoutineLock = false; namespace { struct SelfRemoving { @@ -51,6 +54,14 @@ void yield_2ms() { yield([]() { return getTimerValueSeconds(0) > started + Time(0.002); }); } +void yield_2ms_with_lock() { + mock().actualCall("yield_2ms"); + started = getTimerValueSeconds(0); + usbLock = true; + yield([]() { return getTimerValueSeconds(0) > started + Time(0.002); }); + usbLock = false; +} + TEST_GROUP(Scheduler){ void setup(){taskManager = TaskManager(); @@ -63,7 +74,7 @@ TEST(Scheduler, schedule) { mock().clear(); // will be called one less time due to the time the sleep takes not being zero mock().expectNCalls(0.01 / 0.001 - 1, "sleep_50ns"); - addRepeatingTask(sleep_50ns, 0, 0.001, 0.001, 0.001, "sleep_50ns"); + addRepeatingTask(sleep_50ns, 0, 0.001, 0.001, 0.001, "sleep_50ns", RESOURCE_NONE); // run the scheduler for just under 10ms, calling the function to sleep 50ns every 1ms taskManager.start(0.0095); mock().checkExpectations(); @@ -72,7 +83,8 @@ TEST(Scheduler, schedule) { TEST(Scheduler, remove) { static SelfRemoving selfRemoving; - TaskID id = addRepeatingTask([]() { selfRemoving.runFiveTimes(); }, 0, 0.001, 0.001, 0.001, "run five times"); + TaskID id = + addRepeatingTask([]() { selfRemoving.runFiveTimes(); }, 0, 0.001, 0.001, 0.001, "run five times", RESOURCE_NONE); selfRemoving.id = id; mock().clear(); // will be called one less time due to the time the sleep takes not being zero @@ -87,7 +99,7 @@ TEST(Scheduler, scheduleOnce) { mock().clear(); // will be called one less time due to the time the sleep takes not being zero mock().expectNCalls(1, "sleep_50ns"); - addOnceTask(sleep_50ns, 0, 0.001, "sleep 50ns"); + addOnceTask(sleep_50ns, 0, 0.001, "sleep 50ns", RESOURCE_NONE); // run the scheduler for just under 10ms, calling the function to sleep 50ns every 1ms taskManager.start(0.0095); mock().checkExpectations(); @@ -97,7 +109,7 @@ TEST(Scheduler, scheduleConditional) { mock().clear(); mock().expectNCalls(1, "sleep_50ns"); // will load as blocked but immediately pass condition - addConditionalTask(sleep_50ns, 0, []() { return true; }, "sleep 50ns"); + addConditionalTask(sleep_50ns, 0, []() { return true; }, "sleep 50ns", RESOURCE_NONE); // run the scheduler for just under 10ms, calling the function to sleep 50ns every 1ms taskManager.start(0.0095); mock().checkExpectations(); @@ -107,7 +119,7 @@ TEST(Scheduler, scheduleConditionalDoesntRun) { mock().clear(); mock().expectNCalls(0, "sleep_50ns"); // will load as blocked but immediately pass condition - addConditionalTask(sleep_50ns, 0, []() { return false; }, "sleep 50ns"); + addConditionalTask(sleep_50ns, 0, []() { return false; }, "sleep 50ns", RESOURCE_NONE); // run the scheduler for just under 10ms, calling the function to sleep 50ns every 1ms taskManager.start(0.0095); mock().checkExpectations(); @@ -117,7 +129,7 @@ TEST(Scheduler, backOffTime) { mock().clear(); // will be called one less time due to the time the sleep takes not being zero mock().expectNCalls(9, "sleep_50ns"); - addRepeatingTask(sleep_50ns, 1, 0.01, 0.001, 1.0, "sleep_50ns"); + addRepeatingTask(sleep_50ns, 1, 0.01, 0.001, 1.0, "sleep_50ns", RESOURCE_NONE); // run the scheduler for just under 10ms, calling the function to sleep 50ns every 1ms taskManager.start(0.1); mock().checkExpectations(); @@ -129,8 +141,8 @@ TEST(Scheduler, scheduleOnceWithRepeating) { mock().expectNCalls(0.01 / 0.001 - 2, "sleep_50ns"); mock().expectNCalls(1, "sleep_2ms"); // every 1ms sleep for 50ns and 10ns - addRepeatingTask(sleep_50ns, 10, 0.001, 0.001, 0.001, "sleep_50ns"); - addOnceTask(sleep_2ms, 11, 0, "sleep 2ms"); + addRepeatingTask(sleep_50ns, 10, 0.001, 0.001, 0.001, "sleep_50ns", RESOURCE_NONE); + addOnceTask(sleep_2ms, 11, 0, "sleep 2ms", RESOURCE_NONE); // run the scheduler for 10ms taskManager.start(0.01); mock().checkExpectations(); @@ -142,8 +154,8 @@ TEST(Scheduler, yield) { mock().expectNCalls(0.01 / 0.001 - 1, "sleep_50ns"); mock().expectNCalls(1, "yield_2ms"); // every 1ms sleep for 50ns and 10ns - addRepeatingTask(sleep_50ns, 10, 0.001, 0.001, 0.001, "sleep_50ns"); - addOnceTask(yield_2ms, 2, 0, "sleep 2ms"); + addRepeatingTask(sleep_50ns, 10, 0.001, 0.001, 0.001, "sleep_50ns", RESOURCE_NONE); + addOnceTask(yield_2ms, 2, 0, "sleep 2ms", RESOURCE_NONE); // run the scheduler for 10ms taskManager.start(0.01); mock().checkExpectations(); @@ -154,11 +166,11 @@ TEST(Scheduler, removeWithPriZero) { mock().expectNCalls((0.01 - 0.002) / 0.001 - 1, "sleep_50ns"); mock().expectNCalls(2, "sleep_2ms"); // every 1ms sleep for 50ns and 10ns - addRepeatingTask(sleep_50ns, 10, 0.001, 0.001, 0.001, "sleep 50ns"); - addRepeatingTask([]() { passMockTime(0.00001); }, 0, 0.001, 0.001, 0.001, "mock time"); - addOnceTask(sleep_2ms, 11, 0.002, "sleep 2 ms"); - addRepeatingTask([]() { passMockTime(0.00003); }, 0, 0.001, 0.001, 0.001, "mock time"); - addOnceTask(sleep_2ms, 11, 0.009, "sleep 2ms"); + addRepeatingTask(sleep_50ns, 10, 0.001, 0.001, 0.001, "sleep 50ns", RESOURCE_NONE); + addRepeatingTask([]() { passMockTime(0.00001); }, 0, 0.001, 0.001, 0.001, "mock time", RESOURCE_NONE); + addOnceTask(sleep_2ms, 11, 0.002, "sleep 2 ms", RESOURCE_NONE); + addRepeatingTask([]() { passMockTime(0.00003); }, 0, 0.001, 0.001, 0.001, "mock time", RESOURCE_NONE); + addOnceTask(sleep_2ms, 11, 0.009, "sleep 2ms", RESOURCE_NONE); // run the scheduler for 10ms taskManager.start(0.01); mock().checkExpectations(); @@ -171,7 +183,7 @@ TEST(Scheduler, tooManyTasks) { mock().expectNCalls(kMaxTasks, "sleep_50ns"); // more than allowed for (int i = 0; i <= kMaxTasks + 10; i++) { - addOnceTask(sleep_50ns, 0, 0.001, "sleep 50ns"); + addOnceTask(sleep_50ns, 0, 0.001, "sleep 50ns", RESOURCE_NONE); } // run the scheduler for 10ms @@ -186,7 +198,7 @@ void reAdd50() { passMockTime(0.00002); numCalls += 1; if (numCalls < 50) { - TaskID id = addOnceTask(reAdd50, 0, 0, "reAdd 50"); + TaskID id = addOnceTask(reAdd50, 0, 0, "reAdd 50", RESOURCE_NONE); } }; /// dynamically schedules more than kMaxTask tasks while remaining under kMaxTasks at all times @@ -194,7 +206,7 @@ TEST(Scheduler, moreThanMaxTotal) { numCalls = 0; mock().clear(); mock().expectNCalls(50, "reAdd50"); - addOnceTask(reAdd50, 0, 0, "reAdd50"); + addOnceTask(reAdd50, 0, 0, "reAdd50", RESOURCE_NONE); taskManager.start(0.01); mock().checkExpectations(); } @@ -205,9 +217,9 @@ TEST(Scheduler, scheduleMultiple) { mock().expectNCalls(0.01 / 0.001 - 1, "sleep_20ns"); mock().expectNCalls(1, "sleep_2ms"); // every 1ms sleep for 50ns and 10ns - addRepeatingTask(sleep_50ns, 10, 0.001, 0.001, 0.001, "sleep 50ns"); - addRepeatingTask(sleep_20ns, 0, 0.001, 0.001, 0.001, "sleep 20ns"); - addOnceTask(sleep_2ms, 11, 0.0094, "sleep 2ms"); + addRepeatingTask(sleep_50ns, 10, 0.001, 0.001, 0.001, "sleep 50ns", RESOURCE_NONE); + addRepeatingTask(sleep_20ns, 0, 0.001, 0.001, 0.001, "sleep 20ns", RESOURCE_NONE); + addOnceTask(sleep_2ms, 11, 0.0094, "sleep 2ms", RESOURCE_NONE); // run the scheduler for 10ms taskManager.start(0.0095); mock().checkExpectations(); @@ -223,13 +235,25 @@ TEST(Scheduler, overSchedule) { mock().expectNCalls(0.006 / 0.001, "sleep_20ns"); // every 1ms sleep for 50ns and 10ns - auto fiftynshandle = addRepeatingTask(sleep_50ns, 10, 0.001, 0.001, 0.001, "sleep 50ns"); - auto tennshandle = addRepeatingTask(sleep_20ns, 0, 0.001, 0.001, 0.001, "sleep 20ns"); - auto twomsHandle = addRepeatingTask(sleep_2ms, 100, 0.001, 0.002, 0.005, "sleep 2ms"); + auto fiftynshandle = addRepeatingTask(sleep_50ns, 10, 0.001, 0.001, 0.001, "sleep 50ns", RESOURCE_NONE); + auto tennshandle = addRepeatingTask(sleep_20ns, 0, 0.001, 0.001, 0.001, "sleep 20ns", RESOURCE_NONE); + auto twomsHandle = addRepeatingTask(sleep_2ms, 100, 0.001, 0.002, 0.005, "sleep 2ms", RESOURCE_NONE); // run the scheduler for 10ms taskManager.start(0.0099); mock().checkExpectations(); }; +TEST(Scheduler, yield_with_lock) { + mock().clear(); + // will be locked out by the usb lock + mock().expectNCalls(0, "sleep_50ns"); + mock().expectNCalls(1, "yield_2ms"); + // every 1ms sleep for 50ns and 10ns + addRepeatingTask(sleep_50ns, 10, 0.0001, 0.0001, 0.0001, "sleep_50ns", RESOURCE_USB); + addOnceTask(yield_2ms_with_lock, 2, 0, "sleep 2ms", RESOURCE_USB); + // run the scheduler for 10ms + taskManager.start(0.002); + mock().checkExpectations(); +}; } // namespace