Skip to content

Commit 27f9fb4

Browse files
Fix ESP32 platform manager shutdown. (#22417)
* Fix ESP32 platform manager shutdown. The QEMU ESP32 tests start and stop the platform manager several times, and the lack of a shutdown implementation was putting things in a bad state. Also adjusts the test runner script to trigger a test failure when we do end up in that bad state: we were passing even though we crashed quite early in the test run and most of the tests did not run. * Address review comments. * Fix unlocking in FreeRTOS _RunEventLoop. Ensure we always start with an empty event queue after FreeRTOS _InitChipStack.
1 parent d746902 commit 27f9fb4

File tree

6 files changed

+79
-27
lines changed

6 files changed

+79
-27
lines changed

.restyled.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ restylers:
9898
- "**/*.c"
9999
- "**/*.cc"
100100
- "**/*.cpp"
101+
- "**/*.ipp"
101102
- "**/*.cxx"
102103
- "**/*.c++"
103104
- "**/*.C"
@@ -137,6 +138,7 @@ restylers:
137138
- "**/*.c"
138139
- "**/*.cc"
139140
- "**/*.cpp"
141+
- "**/*.ipp"
140142
- "**/*.cxx"
141143
- "**/*.c++"
142144
- "**/*.C"

src/include/platform/internal/GenericPlatformManagerImpl_FreeRTOS.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ class GenericPlatformManagerImpl_FreeRTOS : public GenericPlatformManagerImpl<Im
6060
protected:
6161
TimeOut_t mNextTimerBaseTime;
6262
TickType_t mNextTimerDurationTicks;
63-
SemaphoreHandle_t mChipStackLock;
64-
QueueHandle_t mChipEventQueue;
65-
TaskHandle_t mEventLoopTask;
63+
SemaphoreHandle_t mChipStackLock = NULL;
64+
QueueHandle_t mChipEventQueue = NULL;
65+
TaskHandle_t mEventLoopTask = NULL;
6666
bool mChipTimerActive;
6767

6868
// ===== Methods that implement the PlatformManager abstract interface.

src/include/platform/internal/GenericPlatformManagerImpl_FreeRTOS.ipp

+45-22
Original file line numberDiff line numberDiff line change
@@ -46,31 +46,50 @@ CHIP_ERROR GenericPlatformManagerImpl_FreeRTOS<ImplClass>::_InitChipStack(void)
4646

4747
vTaskSetTimeOutState(&mNextTimerBaseTime);
4848
mNextTimerDurationTicks = 0;
49-
mEventLoopTask = NULL;
50-
mChipTimerActive = false;
49+
// TODO: This nulling out of mEventLoopTask should happen when we shut down
50+
// the task, not here!
51+
mEventLoopTask = NULL;
52+
mChipTimerActive = false;
5153

54+
// We support calling Shutdown followed by InitChipStack, because some tests
55+
// do that. To keep things simple for existing consumers, we keep not
56+
// destroying our lock and queue in shutdown, but rather check whether they
57+
// already exist here before trying to create them.
58+
59+
if (mChipStackLock == NULL)
60+
{
5261
#if defined(CHIP_CONFIG_FREERTOS_USE_STATIC_SEMAPHORE) && CHIP_CONFIG_FREERTOS_USE_STATIC_SEMAPHORE
53-
mChipStackLock = xSemaphoreCreateMutexStatic(&mChipStackLockMutex);
62+
mChipStackLock = xSemaphoreCreateMutexStatic(&mChipStackLockMutex);
5463
#else
55-
mChipStackLock = xSemaphoreCreateMutex();
64+
mChipStackLock = xSemaphoreCreateMutex();
5665
#endif // CHIP_CONFIG_FREERTOS_USE_STATIC_SEMAPHORE
5766

58-
if (mChipStackLock == NULL)
59-
{
60-
ChipLogError(DeviceLayer, "Failed to create CHIP stack lock");
61-
ExitNow(err = CHIP_ERROR_NO_MEMORY);
67+
if (mChipStackLock == NULL)
68+
{
69+
ChipLogError(DeviceLayer, "Failed to create CHIP stack lock");
70+
ExitNow(err = CHIP_ERROR_NO_MEMORY);
71+
}
6272
}
6373

74+
if (mChipEventQueue == NULL)
75+
{
6476
#if defined(CHIP_CONFIG_FREERTOS_USE_STATIC_QUEUE) && CHIP_CONFIG_FREERTOS_USE_STATIC_QUEUE
65-
mChipEventQueue =
66-
xQueueCreateStatic(CHIP_DEVICE_CONFIG_MAX_EVENT_QUEUE_SIZE, sizeof(ChipDeviceEvent), mEventQueueBuffer, &mEventQueueStruct);
77+
mChipEventQueue = xQueueCreateStatic(CHIP_DEVICE_CONFIG_MAX_EVENT_QUEUE_SIZE, sizeof(ChipDeviceEvent), mEventQueueBuffer,
78+
&mEventQueueStruct);
6779
#else
68-
mChipEventQueue = xQueueCreate(CHIP_DEVICE_CONFIG_MAX_EVENT_QUEUE_SIZE, sizeof(ChipDeviceEvent));
80+
mChipEventQueue = xQueueCreate(CHIP_DEVICE_CONFIG_MAX_EVENT_QUEUE_SIZE, sizeof(ChipDeviceEvent));
6981
#endif
70-
if (mChipEventQueue == NULL)
82+
if (mChipEventQueue == NULL)
83+
{
84+
ChipLogError(DeviceLayer, "Failed to allocate CHIP event queue");
85+
ExitNow(err = CHIP_ERROR_NO_MEMORY);
86+
}
87+
}
88+
else
7189
{
72-
ChipLogError(DeviceLayer, "Failed to allocate CHIP event queue");
73-
ExitNow(err = CHIP_ERROR_NO_MEMORY);
90+
// Clear out any events that might be stuck in the queue, so we start
91+
// with a clean slate, as if we had just re-created the queue.
92+
xQueueReset(mChipEventQueue);
7493
}
7594

7695
mShouldRunEventLoop.store(false);
@@ -145,7 +164,7 @@ void GenericPlatformManagerImpl_FreeRTOS<ImplClass>::_RunEventLoop(void)
145164
ChipDeviceEvent event;
146165

147166
// Lock the CHIP stack.
148-
Impl()->LockChipStack();
167+
StackLock lock;
149168

150169
bool oldShouldRunEventLoop = false;
151170
if (!mShouldRunEventLoop.compare_exchange_strong(oldShouldRunEventLoop /* expected */, true /* desired */))
@@ -198,13 +217,13 @@ void GenericPlatformManagerImpl_FreeRTOS<ImplClass>::_RunEventLoop(void)
198217
waitTime = portMAX_DELAY;
199218
}
200219

201-
// Unlock the CHIP stack, allowing other threads to enter CHIP while the event loop thread is sleeping.
202-
Impl()->UnlockChipStack();
203-
204-
BaseType_t eventReceived = xQueueReceive(mChipEventQueue, &event, waitTime);
205-
206-
// Lock the CHIP stack.
207-
Impl()->LockChipStack();
220+
BaseType_t eventReceived = pdFALSE;
221+
{
222+
// Unlock the CHIP stack, allowing other threads to enter CHIP while
223+
// the event loop thread is sleeping.
224+
StackUnlock unlock;
225+
eventReceived = xQueueReceive(mChipEventQueue, &event, waitTime);
226+
}
208227

209228
// If an event was received, dispatch it. Continue receiving events from the queue and
210229
// dispatching them until the queue is empty.
@@ -236,6 +255,9 @@ void GenericPlatformManagerImpl_FreeRTOS<ImplClass>::EventLoopTaskMain(void * ar
236255
{
237256
ChipLogDetail(DeviceLayer, "CHIP task running");
238257
static_cast<GenericPlatformManagerImpl_FreeRTOS<ImplClass> *>(arg)->Impl()->RunEventLoop();
258+
// TODO: At this point, should we not
259+
// vTaskDelete(static_cast<GenericPlatformManagerImpl_FreeRTOS<ImplClass> *>(arg)->mEventLoopTask)?
260+
// Or somehow get our caller to do it once this thread is joined?
239261
}
240262

241263
template <class ImplClass>
@@ -275,6 +297,7 @@ void GenericPlatformManagerImpl_FreeRTOS<ImplClass>::PostEventFromISR(const Chip
275297
template <class ImplClass>
276298
void GenericPlatformManagerImpl_FreeRTOS<ImplClass>::_Shutdown(void)
277299
{
300+
GenericPlatformManagerImpl<ImplClass>::_Shutdown();
278301
}
279302

280303
template <class ImplClass>

src/platform/ESP32/PlatformManagerImpl.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ void PlatformManagerImpl::_Shutdown()
150150
}
151151

152152
Internal::GenericPlatformManagerImpl_FreeRTOS<PlatformManagerImpl>::_Shutdown();
153+
154+
esp_event_loop_delete_default();
153155
}
154156

155157
void PlatformManagerImpl::HandleESPSystemEvent(void * arg, esp_event_base_t eventBase, int32_t eventId, void * eventData)

src/system/tests/BUILD.gn

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ chip_test_suite("tests") {
3131
"TestTimeSource.cpp",
3232
]
3333

34-
if (chip_device_platform != "esp32" && chip_device_platform != "fake") {
34+
if (chip_device_platform != "fake") {
3535
test_sources += [ "TestSystemScheduleWork.cpp" ]
3636
}
3737

src/test_driver/esp32/run_qemu_image.py

+26-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import click
1818
import logging
1919
import os
20+
import re
2021
import sys
2122
import subprocess
2223

@@ -104,19 +105,41 @@ def main(log_level, no_log_timestamps, image, file_image_list, qemu, verbose):
104105
(path, status.returncode))
105106

106107
# Parse output of the unit test. Generally expect things like:
108+
# I (3034) CHIP-tests: Starting CHIP tests!
109+
# ...
110+
# Various test lines, none ending with "] : FAILED"
111+
# ...
107112
# Failed Tests: 0 / 5
108113
# Failed Asserts: 0 / 77
109114
# I (3034) CHIP-tests: CHIP test status: 0
115+
in_test = False
110116
for line in output.split('\n'):
117+
if line.endswith('CHIP-tests: Starting CHIP tests!'):
118+
in_test = True
119+
111120
if line.startswith('Failed Tests:') and not line.startswith('Failed Tests: 0 /'):
112121
raise Exception("Tests seem failed: %s" % line)
113122

114123
if line.startswith('Failed Asserts:') and not line.startswith('Failed Asserts: 0 /'):
115124
raise Exception("Asserts seem failed: %s" % line)
116125

117-
if 'CHIP test status: ' in line and 'CHIP test status: 0' not in line:
126+
if 'CHIP-tests: CHIP test status: 0' in line:
127+
in_test = False
128+
elif 'CHIP-tests: CHIP test status: ' in line:
118129
raise Exception("CHIP test status is NOT 0: %s" % line)
119130

131+
# Ignore FAILED messages not in the middle of a test, to reduce
132+
# the chance of false posisitves from other logging.
133+
if in_test and re.match('.*] : FAILED$', line):
134+
raise Exception("Step failed: %s" % line)
135+
136+
# TODO: Figure out why exit(0) in man_app.cpp's tester_task is aborting and fix that.
137+
if in_test and line.startswith('abort() was called at PC'):
138+
raise Exception("Unexpected crash: %s" % line)
139+
140+
if in_test:
141+
raise Exception('Not expected to be in the middle of a test when the log ends')
142+
120143
if verbose:
121144
print("========== TEST OUTPUT BEGIN ============")
122145
print(output)
@@ -125,7 +148,9 @@ def main(log_level, no_log_timestamps, image, file_image_list, qemu, verbose):
125148
logging.info("Image %s PASSED", path)
126149
except:
127150
# make sure output is visible in stdout
151+
print("========== TEST OUTPUT BEGIN ============")
128152
print(output)
153+
print("========== TEST OUTPUT END ============")
129154
raise
130155

131156

0 commit comments

Comments
 (0)