Skip to content

Add NimBLEUtils::runOnHostTask for safely running callables on the Ni… - #1209

Draft
h2zero wants to merge 1 commit into
masterfrom
run-on-host
Draft

h2zero wants to merge 1 commit into
masterfrom
run-on-host

Conversation

@h2zero

@h2zero h2zero commented Nov 1, 2026

Copy link
Copy Markdown
Owner

…mBLE host task

Summary

  • Adds a public utility, bool NimBLEUtils::runOnHostTask(std::function<void()> func). It lets library users run code on the NimBLE host task. Callers capture whatever context they need in the callable instead of passing a void* plus a lifetime contract.

Behavior

  • Already on the host task: func runs immediately and the call returns true.
  • Any other task: func is moved into a heap-owned context and queued on the default NimBLE event queue. The host task runs it asynchronously, then frees the context.
  • Host task not running (before NimBLEDevice::init() or after deinit): the call returns false and the callable is dropped.
  • Empty std::function: returns false.

Design notes

  • Ownership: the callable and its ble_npl_event live in one heap-allocated HostTaskContext. This replaces a stack-local event and a pointer to the caller's std::function, which were both dangling hazards.
  • Captured state: values captured by the callable are copied or moved into the context. Anything captured by reference or raw pointer must stay valid until the callable runs. Use values or a shared_ptr when lifetime is uncertain.
  • Pending-task tracking: queued contexts are kept in a spinlock-protected intrusive list and unlinked in executeHostTask.
  • Shutdown: NimBLEUtils::deinit() calls the new drainHostTasks(). It clears the host-task handle and removes queued events with ble_npl_eventq_remove. It then frees the contexts without running them. Queueing and the handle check share one lock, so a call racing with deinit is either rejected or drained, never leaked.

API contract

  • The callable runs asynchronously unless the caller is already on the host task. This is not a completion or wait mechanism.
  • The callable must not throw. It runs inside NimBLE's C event loop.
  • Work queued but not yet run when the stack is deinitialized is discarded.
  • A false return means the work was not run or queued.

…mBLE host task

Summary
* Adds a public utility, bool NimBLEUtils::runOnHostTask(std::function<void()> func). It lets library users run code on the NimBLE host task. Callers capture whatever context they need in the callable instead of passing a void* plus a lifetime contract.

Behavior
* Already on the host task: func runs immediately and the call returns true.
* Any other task: func is moved into a heap-owned context and queued on the default NimBLE event queue. The host task runs it asynchronously, then frees the context.
* Host task not running (before NimBLEDevice::init() or after deinit): the call returns false and the callable is dropped.
* Empty std::function: returns false.

Design notes
* Ownership: the callable and its ble_npl_event live in one heap-allocated HostTaskContext. This replaces a stack-local event and a pointer to the caller's std::function, which were both dangling hazards.
* Captured state: values captured by the callable are copied or moved into the context. Anything captured by reference or raw pointer must stay valid until the callable runs. Use values or a shared_ptr when lifetime is uncertain.
* Pending-task tracking: queued contexts are kept in a spinlock-protected intrusive list and unlinked in executeHostTask.
* Shutdown: NimBLEUtils::deinit() calls the new drainHostTasks(). It clears the host-task handle and removes queued events with ble_npl_eventq_remove. It then frees the contexts without running them. Queueing and the handle check share one lock, so a call racing with deinit is either rejected or drained, never leaked.

API contract
* The callable runs asynchronously unless the caller is already on the host task. This is not a completion or wait mechanism.
* The callable must not throw. It runs inside NimBLE's C event loop.
* Work queued but not yet run when the stack is deinitialized is discarded.
* A false return means the work was not run or queued.
@coderabbitai

coderabbitai Bot commented Nov 1, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

This branch has not been deployed

No deployments
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