Skip to content

Commit c3d0590

Browse files
mark9064NeroBurner
authored andcommitted
Refactor SystemTask state handling for resilience
State transitions now happen immediately where possible This simplifies state management in general, and prevents bugs such as the chime issue from occurring in the first place
1 parent b3756e4 commit c3d0590

5 files changed

Lines changed: 100 additions & 88 deletions

File tree

src/components/ble/DfuService.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,11 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf* om) {
124124
bootloaderSize,
125125
applicationSize);
126126

127-
// wait until SystemTask has finished waking up all devices
128-
while (systemTask.IsSleeping()) {
129-
vTaskDelay(50); // 50ms
127+
// Wait until SystemTask has disabled sleeping
128+
// This isn't quite correct, as we don't actually know
129+
// if BleFirmwareUpdateStarted has been received yet
130+
while (!systemTask.IsSleepDisabled()) {
131+
vTaskDelay(pdMS_TO_TICKS(5));
130132
}
131133

132134
dfuImage.Erase();

src/components/ble/NimbleController.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,9 +454,15 @@ void NimbleController::PersistBond(struct ble_gap_conn_desc& desc) {
454454
/* Wakeup Spi and SpiNorFlash before accessing the file system
455455
* This should be fixed in the FS driver
456456
*/
457-
systemTask.PushMessage(Pinetime::System::Messages::GoToRunning);
458457
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
459-
vTaskDelay(10);
458+
459+
// This isn't quite correct
460+
// SystemTask could receive EnableSleeping right after passing this check
461+
// We need some guarantee that the SystemTask has processed the above message
462+
// before we can continue
463+
while (!systemTask.IsSleepDisabled()) {
464+
vTaskDelay(pdMS_TO_TICKS(5));
465+
}
460466

461467
lfs_file_t file_p;
462468

src/displayapp/DisplayApp.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,20 @@ void DisplayApp::Refresh() {
255255
isDimmed = true;
256256
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
257257
}
258-
if (IsPastSleepTime()) {
259-
systemTask->PushMessage(System::Messages::GoToSleep);
260-
state = States::Idle;
258+
if (IsPastSleepTime() && uxQueueMessagesWaiting(msgQueue) == 0) {
259+
PushMessageToSystemTask(System::Messages::GoToSleep);
260+
// Can't set state to Idle here, something may send
261+
// DisableSleeping before this GoToSleep arrives
262+
// Instead we check we have no messages queued before sending GoToSleep
263+
// This works as the SystemTask is higher priority than DisplayApp
264+
// As soon as we send GoToSleep, SystemTask pre-empts DisplayApp
265+
// Whenever DisplayApp is running again, it is guaranteed that
266+
// SystemTask has handled the message
267+
// If it responded, we will have a GoToSleep waiting in the queue
268+
// By checking that there are no messages in the queue, we avoid
269+
// resending GoToSleep when we already have a response
270+
// SystemTask is resilient to duplicate messages, this is an
271+
// optimisation to reduce pressure on the message queues
261272
}
262273
} else if (isDimmed) {
263274
isDimmed = false;
@@ -273,6 +284,9 @@ void DisplayApp::Refresh() {
273284
if (xQueueReceive(msgQueue, &msg, queueTimeout) == pdTRUE) {
274285
switch (msg) {
275286
case Messages::GoToSleep:
287+
if (state != States::Running) {
288+
break;
289+
}
276290
while (brightnessController.Level() != Controllers::BrightnessController::Levels::Low) {
277291
brightnessController.Lower();
278292
vTaskDelay(100);
@@ -307,6 +321,9 @@ void DisplayApp::Refresh() {
307321
lv_disp_trig_activity(nullptr);
308322
break;
309323
case Messages::GoToRunning:
324+
if (state == States::Running) {
325+
break;
326+
}
310327
if (settingsController.GetAlwaysOnDisplay()) {
311328
lcd.LowPowerOff();
312329
} else {

src/systemtask/SystemTask.cpp

Lines changed: 64 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -196,29 +196,11 @@ void SystemTask::Work() {
196196
}
197197
break;
198198
case Messages::DisableSleeping:
199+
GoToRunning();
199200
doNotGoToSleep = true;
200201
break;
201202
case Messages::GoToRunning:
202-
// SPI doesn't go to sleep for always on mode
203-
if (!settingsController.GetAlwaysOnDisplay()) {
204-
spi.Wakeup();
205-
}
206-
207-
// Double Tap needs the touch screen to be in normal mode
208-
if (!settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) {
209-
touchPanel.Wakeup();
210-
}
211-
212-
spiNorFlash.Wakeup();
213-
214-
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToRunning);
215-
heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::WakeUp);
216-
217-
if (bleController.IsRadioEnabled() && !bleController.IsConnected()) {
218-
nimbleController.RestartFastAdv();
219-
}
220-
221-
state = SystemTaskState::Running;
203+
GoToRunning();
222204
break;
223205
case Messages::TouchWakeUp: {
224206
if (touchHandler.ProcessTouchInfo(touchPanel.GetTouchInfo())) {
@@ -235,13 +217,7 @@ void SystemTask::Work() {
235217
break;
236218
}
237219
case Messages::GoToSleep:
238-
if (doNotGoToSleep) {
239-
break;
240-
}
241-
state = SystemTaskState::GoingToSleep; // Already set in PushMessage()
242-
NRF_LOG_INFO("[systemtask] Going to sleep");
243-
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToSleep);
244-
heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::GoToSleep);
220+
GoToSleep();
245221
break;
246222
case Messages::OnNewTime:
247223
if (alarmController.State() == Controllers::AlarmController::AlarmState::Set) {
@@ -250,16 +226,14 @@ void SystemTask::Work() {
250226
break;
251227
case Messages::OnNewNotification:
252228
if (settingsController.GetNotificationStatus() == Pinetime::Controllers::Settings::Notification::On) {
253-
if (state == SystemTaskState::Sleeping) {
229+
if (IsSleeping()) {
254230
GoToRunning();
255231
}
256232
displayApp.PushMessage(Pinetime::Applications::Display::Messages::NewNotification);
257233
}
258234
break;
259235
case Messages::SetOffAlarm:
260-
if (state == SystemTaskState::Sleeping) {
261-
GoToRunning();
262-
}
236+
GoToRunning();
263237
displayApp.PushMessage(Pinetime::Applications::Display::Messages::AlarmTriggered);
264238
break;
265239
case Messages::BleConnected:
@@ -268,10 +242,8 @@ void SystemTask::Work() {
268242
bleDiscoveryTimer = 5;
269243
break;
270244
case Messages::BleFirmwareUpdateStarted:
245+
GoToRunning();
271246
doNotGoToSleep = true;
272-
if (state == SystemTaskState::Sleeping) {
273-
GoToRunning();
274-
}
275247
displayApp.PushMessage(Pinetime::Applications::Display::Messages::BleFirmwareUpdateStarted);
276248
break;
277249
case Messages::BleFirmwareUpdateFinished:
@@ -282,10 +254,8 @@ void SystemTask::Work() {
282254
break;
283255
case Messages::StartFileTransfer:
284256
NRF_LOG_INFO("[systemtask] FS Started");
257+
GoToRunning();
285258
doNotGoToSleep = true;
286-
if (state == SystemTaskState::Sleeping) {
287-
GoToRunning();
288-
}
289259
// TODO add intent of fs access icon or something
290260
break;
291261
case Messages::StopFileTransfer:
@@ -318,6 +288,13 @@ void SystemTask::Work() {
318288
HandleButtonAction(action);
319289
} break;
320290
case Messages::OnDisplayTaskSleeping:
291+
// The state was set to GoingToSleep when GoToSleep() was called
292+
// If the state is no longer GoingToSleep, we have since transitioned back to Running
293+
// In this case absorb the OnDisplayTaskSleeping
294+
// as DisplayApp is about to receive GoToRunning
295+
if (state != SystemTaskState::GoingToSleep) {
296+
break;
297+
}
321298
if (BootloaderVersion::IsValid()) {
322299
// First versions of the bootloader do not expose their version and cannot initialize the SPI NOR FLASH
323300
// if it's in sleep mode. Avoid bricked device by disabling sleep mode on these versions.
@@ -346,37 +323,23 @@ void SystemTask::Work() {
346323
if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep &&
347324
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::Hours &&
348325
alarmController.State() != AlarmController::AlarmState::Alerting) {
349-
// if sleeping, we can't send a chime to displayApp yet (SPI flash switched off)
350-
// request running first and repush the chime message
351-
if (state == SystemTaskState::Sleeping) {
352-
GoToRunning();
353-
PushMessage(msg);
354-
} else {
355-
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
356-
}
326+
GoToRunning();
327+
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
357328
}
358329
break;
359330
case Messages::OnNewHalfHour:
360331
using Pinetime::Controllers::AlarmController;
361332
if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep &&
362333
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::HalfHours &&
363334
alarmController.State() != AlarmController::AlarmState::Alerting) {
364-
// if sleeping, we can't send a chime to displayApp yet (SPI flash switched off)
365-
// request running first and repush the chime message
366-
if (state == SystemTaskState::Sleeping) {
367-
GoToRunning();
368-
PushMessage(msg);
369-
} else {
370-
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
371-
}
335+
GoToRunning();
336+
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
372337
}
373338
break;
374339
case Messages::OnChargingEvent:
375340
batteryController.ReadPowerState();
341+
GoToRunning();
376342
displayApp.PushMessage(Applications::Display::Messages::OnChargingEvent);
377-
if (state == SystemTaskState::Sleeping) {
378-
GoToRunning();
379-
}
380343
break;
381344
case Messages::MeasureBatteryTimerExpired:
382345
batteryController.MeasureVoltage();
@@ -385,9 +348,7 @@ void SystemTask::Work() {
385348
nimbleController.NotifyBatteryLevel(batteryController.PercentRemaining());
386349
break;
387350
case Messages::OnPairing:
388-
if (state == SystemTaskState::Sleeping) {
389-
GoToRunning();
390-
}
351+
GoToRunning();
391352
displayApp.PushMessage(Pinetime::Applications::Display::Messages::ShowPairingKey);
392353
break;
393354
case Messages::BleRadioEnableToggle:
@@ -422,14 +383,50 @@ void SystemTask::Work() {
422383
#pragma clang diagnostic pop
423384
}
424385

425-
void SystemTask::UpdateMotion() {
426-
if (state == SystemTaskState::GoingToSleep || state == SystemTaskState::WakingUp) {
386+
void SystemTask::GoToRunning() {
387+
if (state == SystemTaskState::Running) {
427388
return;
428389
}
390+
// SPI doesn't go to sleep for always on mode
391+
if (!settingsController.GetAlwaysOnDisplay()) {
392+
spi.Wakeup();
393+
}
394+
395+
// Double Tap needs the touch screen to be in normal mode
396+
if (!settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) {
397+
touchPanel.Wakeup();
398+
}
429399

430-
if (state == SystemTaskState::Sleeping && !(settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist) ||
431-
settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::Shake) ||
432-
motionController.GetService()->IsMotionNotificationSubscribed())) {
400+
spiNorFlash.Wakeup();
401+
402+
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToRunning);
403+
heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::WakeUp);
404+
405+
if (bleController.IsRadioEnabled() && !bleController.IsConnected()) {
406+
nimbleController.RestartFastAdv();
407+
}
408+
409+
state = SystemTaskState::Running;
410+
};
411+
412+
void SystemTask::GoToSleep() {
413+
if (IsSleeping()) {
414+
return;
415+
}
416+
if (IsSleepDisabled()) {
417+
return;
418+
}
419+
NRF_LOG_INFO("[systemtask] Going to sleep");
420+
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToSleep);
421+
heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::GoToSleep);
422+
423+
state = SystemTaskState::GoingToSleep;
424+
};
425+
426+
void SystemTask::UpdateMotion() {
427+
if (IsSleeping() && !(settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist) ||
428+
settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::Shake) ||
429+
motionController.GetService()->IsMotionNotificationSubscribed())) {
433430
return;
434431
}
435432

@@ -452,7 +449,7 @@ void SystemTask::UpdateMotion() {
452449
}
453450
if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::LowerWrist) && state == SystemTaskState::Running &&
454451
motionController.ShouldLowerSleep()) {
455-
PushMessage(Messages::GoToSleep);
452+
GoToSleep();
456453
}
457454
}
458455

@@ -468,7 +465,7 @@ void SystemTask::HandleButtonAction(Controllers::ButtonActions action) {
468465
switch (action) {
469466
case Actions::Click:
470467
// If the first action after fast wakeup is a click, it should be ignored.
471-
if (!fastWakeUpDone && state != SystemTaskState::GoingToSleep) {
468+
if (!fastWakeUpDone) {
472469
displayApp.PushMessage(Applications::Display::Messages::ButtonPushed);
473470
}
474471
break;
@@ -488,17 +485,10 @@ void SystemTask::HandleButtonAction(Controllers::ButtonActions action) {
488485
fastWakeUpDone = false;
489486
}
490487

491-
void SystemTask::GoToRunning() {
492-
if (state == SystemTaskState::Sleeping) {
493-
state = SystemTaskState::WakingUp;
494-
PushMessage(Messages::GoToRunning);
495-
}
496-
}
497-
498488
void SystemTask::OnTouchEvent() {
499489
if (state == SystemTaskState::Running) {
500490
PushMessage(Messages::OnTouchEvent);
501-
} else if (state == SystemTaskState::Sleeping) {
491+
} else {
502492
if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::SingleTap) or
503493
settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) {
504494
PushMessage(Messages::TouchWakeUp);
@@ -507,10 +497,6 @@ void SystemTask::OnTouchEvent() {
507497
}
508498

509499
void SystemTask::PushMessage(System::Messages msg) {
510-
if (msg == Messages::GoToSleep && !doNotGoToSleep) {
511-
state = SystemTaskState::GoingToSleep;
512-
}
513-
514500
if (in_isr()) {
515501
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
516502
xQueueSendFromISR(systemTasksMsgQueue, &msg, &xHigherPriorityTaskWoken);

src/systemtask/SystemTask.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ namespace Pinetime {
5252
namespace System {
5353
class SystemTask {
5454
public:
55-
enum class SystemTaskState { Sleeping, Running, GoingToSleep, WakingUp };
55+
enum class SystemTaskState { Sleeping, Running, GoingToSleep };
5656
SystemTask(Drivers::SpiMaster& spi,
5757
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
5858
Drivers::TwiMaster& twiMaster,
@@ -88,7 +88,7 @@ namespace Pinetime {
8888
};
8989

9090
bool IsSleeping() const {
91-
return state == SystemTaskState::Sleeping || state == SystemTaskState::WakingUp;
91+
return state != SystemTaskState::Running;
9292
}
9393

9494
private:
@@ -131,6 +131,7 @@ namespace Pinetime {
131131
bool fastWakeUpDone = false;
132132

133133
void GoToRunning();
134+
void GoToSleep();
134135
void UpdateMotion();
135136
bool stepCounterMustBeReset = false;
136137
static constexpr TickType_t batteryMeasurementPeriod = pdMS_TO_TICKS(10 * 60 * 1000);

0 commit comments

Comments
 (0)