3#include "platforms/is_platform.h"
17bool core1_separate_stack =
true;
21constexpr int kRpConcurrencyIterations = 100;
22constexpr uint32_t kRpConcurrencyDeadlineMs = 1500;
23constexpr uint32_t kRpCore1ReadyTimeoutMs = 500;
24constexpr uint32_t kRpCore1DoneTimeoutMs = 2000;
27constexpr uint32_t kRpConcurrencyWatchdogBudgetMs = 4000;
29 kRpCore1DoneTimeoutMs <=
30 kRpConcurrencyWatchdogBudgetMs,
31 "RP concurrency diagnostics exceed the watchdog-safe budget");
43int gRpSharedCounter = 0;
45int incrementRpSharedCounter(
int iterations) {
48 while (completed < iterations &&
50 if ((completed & 1) != 0) {
51 if (!gRpCounterMutex.try_lock()) {
55 gRpCounterMutex.lock();
58 gRpCounterMutex.unlock();
67void setup1() { gRpCore1Ready.store(
true); }
70 gRpCore1LoopCount.fetch_add(1);
71 if (!gRpCore1Request.try_acquire()) {
78 const uint32_t generation = gRpRequestGeneration.load();
79 gRpCore1Completed.store(incrementRpSharedCounter(gRpCore1Iterations.load()));
80 gRpCompletedGeneration.store(generation);
81 gRpRequestInFlight.store(
false);
82 gRpCore1Done.release();
90 response.set(
"backend",
"pico-sdk-mutex+arduino-core1");
93 while (!gRpCore1Ready.load() &&
static_cast<int32_t>(
millis() - readyDeadline) < 0) {
96 if (!gRpCore1Ready.load()) {
98 response.set(
"error",
"RP core1 did not start");
102 while (gRpCore1Done.try_acquire()) {}
103 if (gRpRequestInFlight.exchange(
true)) {
105 response.set(
"error",
"RP core1 still owns the prior concurrency request");
108 if (!gRpCounterMutex.try_lock()) {
109 gRpRequestInFlight.store(
false);
111 response.set(
"error",
"RP mutex unavailable before contention test");
114 gRpSharedCounter = 0;
115 gRpCounterMutex.unlock();
117 gRpCore1Iterations.store(kRpConcurrencyIterations);
118 gRpCore1Completed.store(0);
119 const uint32_t generation = gRpRequestGeneration.fetch_add(1) + 1;
120 const uint32_t core1LoopCountBefore = gRpCore1LoopCount.load();
121 gRpCore1Request.release();
122 const int core0Completed =
123 incrementRpSharedCounter(kRpConcurrencyIterations);
125 bool core1Done =
false;
128 if (gRpCore1Done.try_acquire()) {
129 if (gRpCompletedGeneration.load() == generation) {
137 if (!gRpCounterMutex.try_lock()) {
139 response.set(
"error",
"RP mutex unavailable after contention test");
142 const int actual = gRpSharedCounter;
143 gRpCounterMutex.unlock();
144 const int core1Completed = gRpCore1Completed.load();
145 const uint32_t core1LoopCountAfter = gRpCore1LoopCount.load();
147 recursiveMutex.lock();
148 const bool recursiveMutexReady = recursiveMutex.try_lock();
149 if (recursiveMutexReady) {
150 recursiveMutex.unlock();
152 recursiveMutex.unlock();
153 const int expected = kRpConcurrencyIterations * 2;
154 response.set(
"success", core1Done &&
155 core0Completed == kRpConcurrencyIterations &&
156 core1Completed == kRpConcurrencyIterations &&
157 actual == expected && recursiveMutexReady);
158 response.set(
"core1Ready", gRpCore1Ready.load());
159 response.set(
"core1Done", core1Done);
160 response.set(
"iterationsPerCore",
static_cast<int64_t
>(kRpConcurrencyIterations));
161 response.set(
"core0Completed",
static_cast<int64_t
>(core0Completed));
162 response.set(
"core1Completed",
static_cast<int64_t
>(core1Completed));
163 response.set(
"core1LoopCountBefore",
static_cast<int64_t
>(core1LoopCountBefore));
164 response.set(
"core1LoopCountAfter",
static_cast<int64_t
>(core1LoopCountAfter));
165 response.set(
"generation",
static_cast<int64_t
>(generation));
166 response.set(
"recursiveMutexReady", recursiveMutexReady);
167 response.set(
"expected",
static_cast<int64_t
>(expected));
168 response.set(
"actual",
static_cast<int64_t
>(actual));
180 response.set(
"success",
false);
181 response.set(
"supported",
false);
182 response.set(
"backend",
"unsupported");
183 response.set(
"reason",
"RP dual-core concurrency is available only on RP2xxx");
static json object() FL_NO_EXCEPT
Platform-independent mutex interface.
fl::json runRpConcurrencyTest()
Exercise the RP dual-core mutex and semaphore backends from both cores.
void delay(u32 ms, bool run_async=true) FL_NO_EXCEPT
Public delay wrapper that keeps bare Arduino delay() preferred after using fl::delay; while still all...
fl::u32 millis()
Universal millisecond timer - returns milliseconds since system startup.
fl::platforms::recursive_mutex recursive_mutex
fl::platforms::counting_semaphore< LeastMaxValue > counting_semaphore
Counting semaphore abstraction for FastLED.
fl::platforms::binary_semaphore binary_semaphore
Binary semaphore abstraction for FastLED.
fl::platforms::mutex mutex
#define FL_STATIC_ASSERT(...)
Portable compile-time assertion wrapper.