fix: Close leaked file descriptors in SleepActivity and web server#869
Conversation
SdFat is configured with DESTRUCTOR_CLOSES_FILE=0, so FsFile objects are not automatically closed when they go out of scope. Several code paths opened BMP files for sleep screen rendering but never closed them before returning: - renderCustomSleepScreen: random image and /sleep.bmp fallback - renderCoverSleepScreen: book cover BMP The web server delete handler also leaked an FsFile when the opened path was valid but not a directory. Co-Authored-By: Claude Opus 4.6 <[email protected]>
|
Have you verified that folder deletion still works? I see it's unticked in your PR body? |
|
I tested it again just to be sure. I can create and delete a folder on the card via the web UI without any problems. Only empty folders can be deleted, if there is a file or subfolder in it, the message appears: Failed to delete: Folder is not empty. Delete contents first. The third point is related to my mistake, which is related to my attempt at localization into Czech, and therefore will not be relevant. |
|
I would be interested in doing researches about enabling |
|
No actionable comments were generated in the recent review. 🎉 📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
🔇 Additional comments (3)
📝 WalkthroughWalkthroughAdds explicit Changes
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes 🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Looks to use about 5K of flash. Functionally, we never actually intend to destruct a file without closing it, and indeed everywhere we open files we try to close them (this PR pointing out the few bad cases). I think we should move forward with enabling the flag, will open a new PR to do it in and merge this regardless. |
…rosspoint-reader#869) - **SleepActivity.cpp**: Add missing `file.close()` calls in 3 code paths that open BMP files for sleep screen rendering but never close them before returning. Affects random custom sleep images, the `/sleep.bmp` fallback, and book cover sleep screens. - **CrossPointWebServer.cpp**: Add missing `dir.close()` in the delete handler when `Storage.open()` returns a valid `FsFile` that is not a directory. SdFat is configured with `DESTRUCTOR_CLOSES_FILE=0`, which means `FsFile` objects are **not** automatically closed when they go out of scope. Every opened file must be explicitly closed. The SleepActivity leaks are particularly impactful because they occur on every sleep cycle. While ESP32 deep sleep clears RAM on wake, these leaks can still affect the current session if sleep screen rendering is triggered multiple times (e.g., cover preview, or if deep sleep fails to engage). The web server leak in `handleDelete()` is a minor edge case (directory path that opens successfully but `isDirectory()` returns false), but it's still worth fixing for correctness. - [x] Verify sleep screen still renders correctly (custom BMP, fallback, cover modes) - [x] Verify folder deletion still works via the web UI - [ ] Monitor free heap before/after sleep screen rendering to confirm no leak 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Jan Bažant <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Dave Allie <[email protected]> (cherry picked from commit 3cb60aa)
…rosspoint-reader#869) ## Summary - **SleepActivity.cpp**: Add missing `file.close()` calls in 3 code paths that open BMP files for sleep screen rendering but never close them before returning. Affects random custom sleep images, the `/sleep.bmp` fallback, and book cover sleep screens. - **CrossPointWebServer.cpp**: Add missing `dir.close()` in the delete handler when `Storage.open()` returns a valid `FsFile` that is not a directory. ## Context SdFat is configured with `DESTRUCTOR_CLOSES_FILE=0`, which means `FsFile` objects are **not** automatically closed when they go out of scope. Every opened file must be explicitly closed. The SleepActivity leaks are particularly impactful because they occur on every sleep cycle. While ESP32 deep sleep clears RAM on wake, these leaks can still affect the current session if sleep screen rendering is triggered multiple times (e.g., cover preview, or if deep sleep fails to engage). The web server leak in `handleDelete()` is a minor edge case (directory path that opens successfully but `isDirectory()` returns false), but it's still worth fixing for correctness. ## Test plan - [x] Verify sleep screen still renders correctly (custom BMP, fallback, cover modes) - [x] Verify folder deletion still works via the web UI - [ ] Monitor free heap before/after sleep screen rendering to confirm no leak 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Jan Bažant <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Dave Allie <[email protected]>
…rosspoint-reader#869) ## Summary - **SleepActivity.cpp**: Add missing `file.close()` calls in 3 code paths that open BMP files for sleep screen rendering but never close them before returning. Affects random custom sleep images, the `/sleep.bmp` fallback, and book cover sleep screens. - **CrossPointWebServer.cpp**: Add missing `dir.close()` in the delete handler when `Storage.open()` returns a valid `FsFile` that is not a directory. ## Context SdFat is configured with `DESTRUCTOR_CLOSES_FILE=0`, which means `FsFile` objects are **not** automatically closed when they go out of scope. Every opened file must be explicitly closed. The SleepActivity leaks are particularly impactful because they occur on every sleep cycle. While ESP32 deep sleep clears RAM on wake, these leaks can still affect the current session if sleep screen rendering is triggered multiple times (e.g., cover preview, or if deep sleep fails to engage). The web server leak in `handleDelete()` is a minor edge case (directory path that opens successfully but `isDirectory()` returns false), but it's still worth fixing for correctness. ## Test plan - [x] Verify sleep screen still renders correctly (custom BMP, fallback, cover modes) - [x] Verify folder deletion still works via the web UI - [ ] Monitor free heap before/after sleep screen rendering to confirm no leak 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Jan Bažant <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Dave Allie <[email protected]>
…rosspoint-reader#869) ## Summary - **SleepActivity.cpp**: Add missing `file.close()` calls in 3 code paths that open BMP files for sleep screen rendering but never close them before returning. Affects random custom sleep images, the `/sleep.bmp` fallback, and book cover sleep screens. - **CrossPointWebServer.cpp**: Add missing `dir.close()` in the delete handler when `Storage.open()` returns a valid `FsFile` that is not a directory. ## Context SdFat is configured with `DESTRUCTOR_CLOSES_FILE=0`, which means `FsFile` objects are **not** automatically closed when they go out of scope. Every opened file must be explicitly closed. The SleepActivity leaks are particularly impactful because they occur on every sleep cycle. While ESP32 deep sleep clears RAM on wake, these leaks can still affect the current session if sleep screen rendering is triggered multiple times (e.g., cover preview, or if deep sleep fails to engage). The web server leak in `handleDelete()` is a minor edge case (directory path that opens successfully but `isDirectory()` returns false), but it's still worth fixing for correctness. ## Test plan - [x] Verify sleep screen still renders correctly (custom BMP, fallback, cover modes) - [x] Verify folder deletion still works via the web UI - [ ] Monitor free heap before/after sleep screen rendering to confirm no leak 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Jan Bažant <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Dave Allie <[email protected]>
…rosspoint-reader#869) ## Summary - **SleepActivity.cpp**: Add missing `file.close()` calls in 3 code paths that open BMP files for sleep screen rendering but never close them before returning. Affects random custom sleep images, the `/sleep.bmp` fallback, and book cover sleep screens. - **CrossPointWebServer.cpp**: Add missing `dir.close()` in the delete handler when `Storage.open()` returns a valid `FsFile` that is not a directory. ## Context SdFat is configured with `DESTRUCTOR_CLOSES_FILE=0`, which means `FsFile` objects are **not** automatically closed when they go out of scope. Every opened file must be explicitly closed. The SleepActivity leaks are particularly impactful because they occur on every sleep cycle. While ESP32 deep sleep clears RAM on wake, these leaks can still affect the current session if sleep screen rendering is triggered multiple times (e.g., cover preview, or if deep sleep fails to engage). The web server leak in `handleDelete()` is a minor edge case (directory path that opens successfully but `isDirectory()` returns false), but it's still worth fixing for correctness. ## Test plan - [x] Verify sleep screen still renders correctly (custom BMP, fallback, cover modes) - [x] Verify folder deletion still works via the web UI - [ ] Monitor free heap before/after sleep screen rendering to confirm no leak 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Jan Bažant <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Dave Allie <[email protected]>
…rosspoint-reader#869) ## Summary - **SleepActivity.cpp**: Add missing `file.close()` calls in 3 code paths that open BMP files for sleep screen rendering but never close them before returning. Affects random custom sleep images, the `/sleep.bmp` fallback, and book cover sleep screens. - **CrossPointWebServer.cpp**: Add missing `dir.close()` in the delete handler when `Storage.open()` returns a valid `FsFile` that is not a directory. ## Context SdFat is configured with `DESTRUCTOR_CLOSES_FILE=0`, which means `FsFile` objects are **not** automatically closed when they go out of scope. Every opened file must be explicitly closed. The SleepActivity leaks are particularly impactful because they occur on every sleep cycle. While ESP32 deep sleep clears RAM on wake, these leaks can still affect the current session if sleep screen rendering is triggered multiple times (e.g., cover preview, or if deep sleep fails to engage). The web server leak in `handleDelete()` is a minor edge case (directory path that opens successfully but `isDirectory()` returns false), but it's still worth fixing for correctness. ## Test plan - [x] Verify sleep screen still renders correctly (custom BMP, fallback, cover modes) - [x] Verify folder deletion still works via the web UI - [ ] Monitor free heap before/after sleep screen rendering to confirm no leak 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Jan Bažant <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Dave Allie <[email protected]>
…rosspoint-reader#869) ## Summary - **SleepActivity.cpp**: Add missing `file.close()` calls in 3 code paths that open BMP files for sleep screen rendering but never close them before returning. Affects random custom sleep images, the `/sleep.bmp` fallback, and book cover sleep screens. - **CrossPointWebServer.cpp**: Add missing `dir.close()` in the delete handler when `Storage.open()` returns a valid `FsFile` that is not a directory. ## Context SdFat is configured with `DESTRUCTOR_CLOSES_FILE=0`, which means `FsFile` objects are **not** automatically closed when they go out of scope. Every opened file must be explicitly closed. The SleepActivity leaks are particularly impactful because they occur on every sleep cycle. While ESP32 deep sleep clears RAM on wake, these leaks can still affect the current session if sleep screen rendering is triggered multiple times (e.g., cover preview, or if deep sleep fails to engage). The web server leak in `handleDelete()` is a minor edge case (directory path that opens successfully but `isDirectory()` returns false), but it's still worth fixing for correctness. ## Test plan - [x] Verify sleep screen still renders correctly (custom BMP, fallback, cover modes) - [x] Verify folder deletion still works via the web UI - [ ] Monitor free heap before/after sleep screen rendering to confirm no leak 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Jan Bažant <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Dave Allie <[email protected]>
…rosspoint-reader#869) ## Summary - **SleepActivity.cpp**: Add missing `file.close()` calls in 3 code paths that open BMP files for sleep screen rendering but never close them before returning. Affects random custom sleep images, the `/sleep.bmp` fallback, and book cover sleep screens. - **CrossPointWebServer.cpp**: Add missing `dir.close()` in the delete handler when `Storage.open()` returns a valid `FsFile` that is not a directory. ## Context SdFat is configured with `DESTRUCTOR_CLOSES_FILE=0`, which means `FsFile` objects are **not** automatically closed when they go out of scope. Every opened file must be explicitly closed. The SleepActivity leaks are particularly impactful because they occur on every sleep cycle. While ESP32 deep sleep clears RAM on wake, these leaks can still affect the current session if sleep screen rendering is triggered multiple times (e.g., cover preview, or if deep sleep fails to engage). The web server leak in `handleDelete()` is a minor edge case (directory path that opens successfully but `isDirectory()` returns false), but it's still worth fixing for correctness. ## Test plan - [x] Verify sleep screen still renders correctly (custom BMP, fallback, cover modes) - [x] Verify folder deletion still works via the web UI - [ ] Monitor free heap before/after sleep screen rendering to confirm no leak 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Jan Bažant <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Dave Allie <[email protected]>
…rosspoint-reader#869) ## Summary - **SleepActivity.cpp**: Add missing `file.close()` calls in 3 code paths that open BMP files for sleep screen rendering but never close them before returning. Affects random custom sleep images, the `/sleep.bmp` fallback, and book cover sleep screens. - **CrossPointWebServer.cpp**: Add missing `dir.close()` in the delete handler when `Storage.open()` returns a valid `FsFile` that is not a directory. ## Context SdFat is configured with `DESTRUCTOR_CLOSES_FILE=0`, which means `FsFile` objects are **not** automatically closed when they go out of scope. Every opened file must be explicitly closed. The SleepActivity leaks are particularly impactful because they occur on every sleep cycle. While ESP32 deep sleep clears RAM on wake, these leaks can still affect the current session if sleep screen rendering is triggered multiple times (e.g., cover preview, or if deep sleep fails to engage). The web server leak in `handleDelete()` is a minor edge case (directory path that opens successfully but `isDirectory()` returns false), but it's still worth fixing for correctness. ## Test plan - [x] Verify sleep screen still renders correctly (custom BMP, fallback, cover modes) - [x] Verify folder deletion still works via the web UI - [ ] Monitor free heap before/after sleep screen rendering to confirm no leak 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Jan Bažant <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Dave Allie <[email protected]>
## Summary * Enable `DESTRUCTOR_CLOSES_FILE` flag * We're never intending to not close files, so if we accidentally leave them open as they're destructured, this will help close them. ## Additional Context * As spotted in #869, there are cases where we were accidentally not closing files Looks to use about 5K of flash. ``` RAM: [=== ] 31.5% (used 103100 bytes from 327680 bytes) Flash: [======= ] 68.9% (used 4513220 bytes from 6553600 bytes) ``` ``` RAM: [=== ] 31.5% (used 103100 bytes from 327680 bytes) Flash: [======= ] 68.9% (used 4518498 bytes from 6553600 bytes) ``` --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? No
Summary
file.close()calls in 3 code paths that open BMP files for sleep screen rendering but never close them before returning. Affects random custom sleep images, the/sleep.bmpfallback, and book cover sleep screens.dir.close()in the delete handler whenStorage.open()returns a validFsFilethat is not a directory.Context
SdFat is configured with
DESTRUCTOR_CLOSES_FILE=0, which meansFsFileobjects are not automatically closed when they go out of scope. Every opened file must be explicitly closed.The SleepActivity leaks are particularly impactful because they occur on every sleep cycle. While ESP32 deep sleep clears RAM on wake, these leaks can still affect the current session if sleep screen rendering is triggered multiple times (e.g., cover preview, or if deep sleep fails to engage).
The web server leak in
handleDelete()is a minor edge case (directory path that opens successfully butisDirectory()returns false), but it's still worth fixing for correctness.Test plan
🤖 Generated with Claude Code