Skip to content

extensible CuContext for callbacks with clock, cl_id and task_id#857

Merged
gbin merged 10 commits into
masterfrom
gbin/clnb
Feb 25, 2026
Merged

extensible CuContext for callbacks with clock, cl_id and task_id#857
gbin merged 10 commits into
masterfrom
gbin/clnb

Conversation

@gbin

@gbin gbin commented Feb 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

This is something that needs to be done before v1 so we can have some backward compatibility headroom (ie so we can add features to the callbacks without breaking them)

And we needed cl_id (copper list seq id) and task_id (numerical and string) in a bunch of use cases where the user wants to know what is ... the context.. of this execution. For example, which camera is it from (task_id), what is the image sequence number (cl_id).

Also added it to the monitor callbacks and UI:
image

Related issues

Changes

Testing

  • just fmt
  • just lint
  • just test
  • optional full just std-ci (if std/runtime paths are impacted)
  • optional full just nostd-ci (if embedded/no_std paths are impacted)
  • Other (please specify):

pro-tip: just with no parameters in the root defaults to just fmt, just lint, and just test.

Checklist

  • I have updated docs or examples where needed
  • I have added or updated tests where needed
  • I have considered platform impact (Linux/macOS/Windows/embedded)
  • I have considered config/logging changes (if applicable)
  • This change is not a breaking change (or I documented it below)

Breaking changes (if any)

major: all the copper tasks in the world but this is to minimize future breakages.

Additional context

This is something that needs to be done before v1 so we can have some
backward compatibility

And we needed cl_id and task_id in a bunch of contexts.
gbin added 3 commits February 23, 2026 11:16
It is everywhere, it needs to be a little bit terse

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces CuContext as an extensible execution context to replace RobotClock in all task and bridge callbacks. This change provides tasks with access to the robot clock, copper list ID (cl_id), and task metadata (task_id), establishing a foundation for adding future context fields without breaking API compatibility.

Changes:

  • Introduced CuContext struct with builder pattern, providing clock access via Deref<Target = RobotClock>, cl_id, and task metadata
  • Updated all task trait signatures (CuTask, CuSrcTask, CuSinkTask) and bridge trait signatures (CuBridge) to accept &CuContext instead of &RobotClock
  • Modified code generation in cu29_derive to create and manage CuContext instances and set current task context before callbacks
  • Updated 60+ example files and all component implementations (sources, sinks, tasks, bridges) to use the new signature

Reviewed changes

Copilot reviewed 92 out of 92 changed files in this pull request and generated no comments.

Show a summary per file
File Description
core/cu29_runtime/src/context.rs New CuContext type with clock, cl_id, and task metadata; implements Deref for backward compatibility
core/cu29_runtime/src/cutask.rs Updated trait signatures for CuTask, CuSrcTask, CuSinkTask to use &CuContext
core/cu29_runtime/src/cubridge.rs Updated CuBridge trait signatures to use &CuContext
core/cu29_derive/src/lib.rs Code generation updates to create CuContext and manage task context
core/cu29_runtime/src/cuasynctask.rs Async task wrapper updated to pass CuContext
core/cu29_runtime/src/simulation.rs Simulation support updated for new signatures
core/cu29/src/lib.rs Export context module in prelude
examples/* (60+ files) All example tasks updated to use &CuContext parameter
components/* (35+ files) All components (sources, sinks, bridges) updated to use &CuContext
Comments suppressed due to low confidence (5)

components/bridges/cu_zenoh_bridge/src/lib.rs:336

  • Variable name shadowing: the parameter _ctx (a CuContext) shadows the local variable ctx (a ZenohContext). This creates confusion and could lead to bugs where the wrong context is referenced. Consider renaming the local variable to runtime_ctx or zenoh_ctx for clarity, similar to how it was done in the receive method (line 365) and in the ROS2 bridge.
    fn send<'a, Payload>(
        &mut self,
        _ctx: &CuContext,
        channel: &'static BridgeChannel<<Self::Tx as BridgeChannelSet>::Id, Payload>,
        msg: &CuMsg<Payload>,
    ) -> CuResult<()>
    where
        Payload: CuMsgPayload + 'a,
    {
        let ctx = self
            .ctx
            .as_mut()
            .ok_or_else(|| CuError::from("ZenohBridge: Context not initialized"))?;

examples/cu_monitoring/src/main.rs:117

  • Inconsistent API: The CuMonitor trait methods start and stop still use &RobotClock instead of &CuContext, which is inconsistent with the changes made to CuTask, CuSrcTask, CuSinkTask, and CuBridge traits. This inconsistency means that monitor callbacks cannot access cl_id() or task_id() like other callbacks can. For API consistency and to meet the PR's goal of providing context to all callbacks, these methods should also be updated to use &CuContext.
    components/bridges/cu_iceoryx2_bridge/src/lib.rs:489
  • Variable name shadowing: the parameter _ctx (a CuContext) shadows the local variable ctx (an IceoryxContext). This creates confusion and could lead to bugs where the wrong context is referenced. Consider renaming the local variable to runtime_ctx or iceoryx_ctx for clarity, similar to how it was done in the receive method at line 545.
    fn start(&mut self, _ctx: &CuContext) -> CuResult<()> {
        let mut builder = NodeBuilder::new();
        if let Some(name) = &self.node_name {
            builder = builder.name(name);
        }
        let node = builder
            .create::<IceoryxService>()
            .map_err(|e| CuError::new_with_cause("Iceoryx2Bridge: Failed to create node", e))?;

        let ctx = IceoryxContext::<Tx::Id, Rx::Id> {
            node,
            tx_channels: Vec::new(),
            rx_channels: Vec::new(),
        };
        self.ctx = Some(Box::new(RuntimeContext::new(ctx)));
        Ok(())

components/bridges/cu_iceoryx2_bridge/src/lib.rs:525

  • Variable name shadowing: the parameter _ctx (a CuContext) shadows the local variable ctx (returned from self.ctx_mut()). This creates confusion and could lead to bugs where the wrong context is referenced. Consider renaming the local variable to runtime_ctx or iceoryx_ctx for clarity, similar to how it was done in the receive method at line 545.
    fn send<'a, Payload>(
        &mut self,
        _ctx: &CuContext,
        channel: &'static BridgeChannel<<Self::Tx as BridgeChannelSet>::Id, Payload>,
        msg: &CuMsg<Payload>,
    ) -> CuResult<()>
    where
        Payload: CuMsgPayload + 'a + 'static,
    {
        let cfg = self.find_tx_config(channel.id()).ok_or_else(|| {
            CuError::from(format!(
                "Iceoryx2Bridge: Unknown Tx channel {:?}",
                channel.id()
            ))
        })?;
        let service = cfg.service.clone();
        let max_payload_bytes = cfg.max_payload_bytes;

        let ctx = self.ctx_mut()?;

        if let Some(tx_channel) =
            Self::find_tx_channel_mut::<Payload>(&mut ctx.tx_channels, channel.id())?
        {
            return tx_channel.send(msg);
        }

        let mut new_channel =
            IceoryxTxChannel::<Payload>::new(&mut ctx.node, &service, max_payload_bytes)?;
        new_channel.send(msg)?;
        ctx.tx_channels.push(IceoryxTxChannelEntry {
            id: channel.id(),
            channel: Box::new(new_channel),
        });
        Ok(())

components/sources/cu_rp_encoder/src/lib.rs:145

  • Unnecessary full context clone: The code clones the entire CuContext (which includes task_ids and other fields) just to access the clock in the interrupt handler. Since the interrupt handler only needs time access via clock.now(), consider cloning just the clock field instead: let clock = ctx.clock.clone();. This is more efficient and makes the intent clearer.
        let clock = ctx.clone();

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@makeecat

Copy link
Copy Markdown
Collaborator

@codex review. and specifically focus on ctx parameter

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 207d570519

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread core/cu29_derive/src/lib.rs

@makeecat makeecat left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Agree with codex review.

This is a potential ambiguity in CuContext::cl_id() lifecycle

CuContext is documented as providing the “current copper-list id”, but generated runtime code passes a context without cl_id set during some callbacks (preprocess,start, stop). In those paths, cl_id is still the default 0, which can be misread as a real CL id.

We can potentially do either of these two options:

  1. Clarify docs/contracts that cl_id is only valid during per-copperlist execution callbacks.
  2. Make absence explicit in API (e.g., cl_id() -> Option<u64> or has_cl_id()), instead of overloading 0.

@gbin

gbin commented Feb 25, 2026

Copy link
Copy Markdown
Collaborator Author

@makeecat fair enough.

It is cleaner to change the semantics a little bit to: this start / stop / preprocess / postprocess where it would be this CL id evn if at the time of the call it might not exist in memory.

pushed a fix, it is cheap too.

@gbin gbin requested a review from makeecat February 25, 2026 14:23
@gbin gbin merged commit 3259cde into master Feb 25, 2026
23 checks passed
@gbin gbin deleted the gbin/clnb branch February 25, 2026 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add the ID of the task to the config passed to the task

3 participants