Skip to content

Replace task noitifcations with semaphores to avoid conflicts.#421

Merged
h2zero merged 1 commit intomasterfrom
taskwait-semaphores
Apr 24, 2026
Merged

Replace task noitifcations with semaphores to avoid conflicts.#421
h2zero merged 1 commit intomasterfrom
taskwait-semaphores

Conversation

@h2zero
Copy link
Copy Markdown
Owner

@h2zero h2zero commented Apr 24, 2026

This will prevent a case where applications may notify a task that is blocking on a BLE operation before it completes.
This can occur when writing to a characteristic in the application and blocking with task notifications until a notification occurs that releases it and the notification arrives before the write response.

Summary by CodeRabbit

  • Chores

    • Removed a deprecated FreeRTOS configuration option.
  • Bug Fixes

    • Ensures full resource cleanup during device deinitialization.
    • Improves task synchronization and error logging for more reliable operation.
  • Refactor

    • Internal synchronization reworked to a centralized semaphore registry for cleaner lifecycle and teardown management.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Arr: Removed the Kconfig integer option and refactored task synchronization to use a centralized semaphore registry inside NimBLEUtils (TaskData + m_taskSemEntries), updated call-sites and member pointer types, and added deleteTaskSems() invoked during device deinit. (≤50 words)

Changes

Cohort / File(s) Summary
Configuration Removal
Kconfig
Deleted config NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT (int, default 31) and its UI/help text.
Core task sync refactor
src/NimBLEUtils.h, src/NimBLEUtils.cpp
Replaced external NimBLETaskData handle model with nested NimBLEUtils::TaskData using TaskSemEntry*. Added semaphore registry (m_taskSemEntries), deleteTaskSems(), selector/allocation of ble_npl_sem in taskWait, updated taskRelease, logging, and semaphore lifecycle changes. Adjusted signatures for taskWait/taskRelease.
Device teardown cleanup
src/NimBLEDevice.cpp
When deinit(clearAll==true) now calls NimBLEUtils::deleteTaskSems() after deleting role-specific objects to clean up registered semaphores.
Consumer updates — clients, channels, GATT, scan
src/NimBLEClient.h, src/NimBLEClient.cpp, src/NimBLEL2CAPChannel.h, src/NimBLEL2CAPChannel.cpp, src/NimBLERemoteCharacteristic.cpp, src/NimBLERemoteService.cpp, src/NimBLERemoteValueAttribute.cpp, src/NimBLEScan.h, src/NimBLEScan.cpp
Replaced usages/forward-declarations of NimBLETaskData with NimBLEUtils::TaskData (member pointers, local task objects, callback arg casts). Minor formatting/string-literal tweaks in scan stats. No other control-flow or API behavior changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🏴‍☠️ Semaphores muster in a registry chest,
Old handles walked the plank, now rest,
deleteTaskSems swabs the deck clean,
Call-sites swapped — no mutiny seen,
Yarrr, the code be lean and keen!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title contains a typo ('noitifcations' instead of 'notifications') and accurately describes the main change: replacing task notifications with semaphores to prevent race conditions.
Docstring Coverage ✅ Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch taskwait-semaphores

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/NimBLEUtils.cpp (1)

138-147: LGTM on the release path, just one pedantic nit.

Lookup-by-taskID, null-check, error log with return-code string — all shipshape. One tiny polish: the logged "task handle" wording in taskWait (Line 123) and "in taskRelease" here be slightly inconsistent; consider unifyin' for easier triage when a user grep's the logs. Yarrr.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NimBLEUtils.cpp` around lines 138 - 147, Log wording is inconsistent
between the task wait and release paths; update the error message in
NimBLEUtils::taskRelease that currently calls NIMBLE_LOGE(LOG_TAG, "Semaphore
lookup failed in taskRelease") to use the same "task handle" phrasing as the
message in NimBLEUtils::taskWait so grepping/troubleshooting is consistent
(locate NimBLEUtils::getTaskSem, taskRelease, taskWait and make the taskRelease
NIMBLE_LOGE message mirror the wording used by taskWait).
src/NimBLEUtils.h (1)

78-89: Aye, a tidy encapsulation — but the registry's cargo hold be unguarded.

The friend declarations and private helpers keep the surface clean, but m_taskSemEntries is a shared mutable container with no synchronization primitive beside it. Since getTaskSem() can be invoked concurrently from app tasks callin' BLE ops on the same device, consider documentin' (or addin') a mutex here to fend off concurrent push_back/iteration. Full concern be filed on NimBLEUtils.cpp.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NimBLEUtils.h` around lines 78 - 89, The static vector m_taskSemEntries
is accessed concurrently by getTaskSem (and deleteTaskSems) and needs
synchronization: add a static mutex (e.g., std::mutex m_taskSemEntriesMutex)
alongside the TaskSemEntry/m_taskSemEntries declarations, include <mutex>, and
guard all accesses/iterations/modifications of m_taskSemEntries inside
getTaskSem and deleteTaskSems (and any other functions touching it) with
std::lock_guard<std::mutex> to prevent concurrent push_back/iteration races.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/NimBLEDevice.cpp`:
- Line 1075: The call to NimBLEUtils::deleteTaskSems() can free semaphores while
other threads may still be blocked in NimBLEUtils::taskWait(); update teardown
so role objects explicitly release waiting tasks before the global semaphore
registry is wiped: in the destructors for NimBLEScan, NimBLEClient (and any
class owning m_pTaskData) call NimBLEUtils::taskRelease(m_pTaskData, /*error*/
true) or equivalent to wake/punt any pending ble_npl_sem_pend, and/or modify
NimBLEUtils::deleteTaskSems() to skip deleting entries whose m_pTaskData
indicates a pending waiter; ensure all waits are released/unwound prior to
removing semaphore memory to avoid dangling penders.

In `@src/NimBLEUtils.cpp`:
- Around line 62-95: The m_taskSemEntries vector is not synchronized and can be
mutated concurrently by getTaskSem() and deleteTaskSems(); protect all accesses
by using a mutex (e.g., a ble_npl_mutex) to avoid races: acquire the mutex at
the start of NimBLEUtils::getTaskSem() and hold it through the lookup and any
creation/push_back (release after insertion), and acquire the same mutex in
NimBLEUtils::deleteTaskSems() while deleting entries and clearing the vector;
ensure you initialize/destroy the mutex in the NimBLEUtils module setup/teardown
so the lock is available when getTaskSem() runs.
- Around line 120-128: In taskWait (use NimBLEUtils::getTaskSem and
ble_npl_sem_pend), drain any leftover semaphore count before the timed wait by
repeatedly performing a non-blocking ble_npl_sem_pend (timeout 0) loop until it
returns not-OK, ensuring the semaphore starts in a non-signaled state for this
wait; then log and perform the actual timed ble_npl_sem_pend(sem, ticks) as now
implemented.
- Around line 90-95: deleteTaskSems currently only deletes the C++ wrapper
(ble_npl_sem) and leaks the underlying FreeRTOS SemaphoreHandle_t; before
deleting each entry in m_taskSemEntries, call vSemaphoreDelete(...) on the
wrapper's underlying handle (e.g., entry.sem->sem or the actual handle field
inside ble_npl_sem) if non-null, then delete the wrapper and finally clear
m_taskSemEntries; reference the ble_npl_sem wrapper, the ble_npl_sem_init
creator, and deleteTaskSems to locate the code and use vSemaphoreDelete to free
the kernel semaphore prior to deleting the wrapper.

---

Nitpick comments:
In `@src/NimBLEUtils.cpp`:
- Around line 138-147: Log wording is inconsistent between the task wait and
release paths; update the error message in NimBLEUtils::taskRelease that
currently calls NIMBLE_LOGE(LOG_TAG, "Semaphore lookup failed in taskRelease")
to use the same "task handle" phrasing as the message in NimBLEUtils::taskWait
so grepping/troubleshooting is consistent (locate NimBLEUtils::getTaskSem,
taskRelease, taskWait and make the taskRelease NIMBLE_LOGE message mirror the
wording used by taskWait).

In `@src/NimBLEUtils.h`:
- Around line 78-89: The static vector m_taskSemEntries is accessed concurrently
by getTaskSem (and deleteTaskSems) and needs synchronization: add a static mutex
(e.g., std::mutex m_taskSemEntriesMutex) alongside the
TaskSemEntry/m_taskSemEntries declarations, include <mutex>, and guard all
accesses/iterations/modifications of m_taskSemEntries inside getTaskSem and
deleteTaskSems (and any other functions touching it) with
std::lock_guard<std::mutex> to prevent concurrent push_back/iteration races.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4c6f045a-c4dd-4bb5-bdf0-88e128a4f16f

📥 Commits

Reviewing files that changed from the base of the PR and between 1c7fe71 and 7000dc1.

📒 Files selected for processing (4)
  • Kconfig
  • src/NimBLEDevice.cpp
  • src/NimBLEUtils.cpp
  • src/NimBLEUtils.h
💤 Files with no reviewable changes (1)
  • Kconfig

Comment thread src/NimBLEDevice.cpp
Comment thread src/NimBLEUtils.cpp Outdated
Comment thread src/NimBLEUtils.cpp Outdated
Comment thread src/NimBLEUtils.cpp Outdated
@h2zero h2zero force-pushed the taskwait-semaphores branch from 7000dc1 to fbd39bb Compare April 24, 2026 19:30
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/NimBLEL2CAPChannel.h`:
- Line 30: Remove the invalid forward declaration "struct
NimBLEUtils::TaskData;" and instead include the full declaration by adding
`#include` "NimBLEUtils.h" to NimBLEL2CAPChannel.h so NimBLEUtils::TaskData is
visible; update the NimBLEL2CAPChannel class members and any construction/usage
(the member that uses NimBLEUtils::TaskData* and the places that create TaskData
instances) to rely on the real definition from NimBLEUtils.h rather than a
nested forward-declare.

In `@src/NimBLEUtils.cpp`:
- Around line 123-131: NimBLEUtils::taskRelease currently dereferences
taskData.m_pSem without nullptr checks and early-returns before clearing inUse
on ble_npl_sem_release failure; fix by first checking if taskData.m_pSem is
nullptr and log/return if so, then call ble_npl_sem_release, and regardless of
the return code always set taskData.m_pSem->inUse = false (after the nullptr
check) so entries aren't left stuck; reference NimBLEUtils::taskRelease,
taskWait (for the init path that may leave m_pSem null), taskData.m_pSem, and
ble_npl_sem_release when locating the change.
- Around line 137-145: NimBLEUtils::deleteTaskSems is leaking the C++ heap
object allocated for each entry->sem; after calling
ble_npl_sem_deinit(entry->sem) you must also delete the semaphore pointer before
deleting the wrapper. Update NimBLEUtils::deleteTaskSems to, for each entry in
m_taskSemEntries, release and deinit the semaphore
(ble_npl_sem_release/ble_npl_sem_deinit) and then delete entry->sem (or check
for non-null and delete) prior to delete entry; finally clear m_taskSemEntries.

In `@src/NimBLEUtils.h`:
- Around line 59-63: The comment says TaskData's documentation mentions a
non-existent member m_pTaskID; update the docblock for struct TaskData to
accurately reflect current fields and behavior: remove the reference to
m_pTaskID and state that taskWait() sets the private m_pSem semaphore (or other
actual members) and that TaskData is used with NimBLEUtils::taskWait() and
NimBLEUtils::taskRelease() to block/unblock tasks; ensure you reference the
struct name TaskData and the methods NimBLEUtils::taskWait() and
NimBLEUtils::taskRelease() so future readers can locate the logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3d2b2048-841d-48be-9d33-42b2cb064a7c

📥 Commits

Reviewing files that changed from the base of the PR and between 7000dc1 and fbd39bb.

📒 Files selected for processing (13)
  • Kconfig
  • src/NimBLEClient.cpp
  • src/NimBLEClient.h
  • src/NimBLEDevice.cpp
  • src/NimBLEL2CAPChannel.cpp
  • src/NimBLEL2CAPChannel.h
  • src/NimBLERemoteCharacteristic.cpp
  • src/NimBLERemoteService.cpp
  • src/NimBLERemoteValueAttribute.cpp
  • src/NimBLEScan.cpp
  • src/NimBLEScan.h
  • src/NimBLEUtils.cpp
  • src/NimBLEUtils.h
💤 Files with no reviewable changes (1)
  • Kconfig
✅ Files skipped from review due to trivial changes (2)
  • src/NimBLEDevice.cpp
  • src/NimBLEL2CAPChannel.cpp

Comment thread src/NimBLEL2CAPChannel.h Outdated
Comment thread src/NimBLEUtils.cpp
Comment thread src/NimBLEUtils.cpp
Comment thread src/NimBLEUtils.h
This will prevent a case where applications may notify a task that is blocking on a BLE operation before it completes.
This can occur when writing to a characteristic in the application and blocking with task notifications until a notification
occurs that releases it and the notification arrives before the write response.
@h2zero h2zero force-pushed the taskwait-semaphores branch from fbd39bb to 16ab3f6 Compare April 24, 2026 20:13
@h2zero
Copy link
Copy Markdown
Owner Author

h2zero commented Apr 24, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@h2zero h2zero merged commit 6b69cb1 into master Apr 24, 2026
66 checks passed
@h2zero h2zero deleted the taskwait-semaphores branch April 24, 2026 21:30
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant