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