{"record":{"id":"0930596e6bbb75fa","repo":"embassy-rs/embassy","slug":"task-is-already-in-use","errorCode":null,"errorMessage":"Task is already in use","messagePattern":"Task is already in use","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"embassy-nrf/src/ppi/dppi.rs","lineNumber":34,"sourceCode":"    }\n}\n\nimpl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {\n    /// Configure PPI channel to trigger both `task1` and `task2` on `event`.\n    pub fn new_one_to_two(ch: Peri<'d, C>, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {\n        Ppi::new_many_to_many(ch, [event], [task1, task2])\n    }\n}\n\nimpl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize>\n    Ppi<'d, C, EVENT_COUNT, TASK_COUNT>\n{\n    /// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires.\n    pub fn new_many_to_many(ch: Peri<'d, C>, events: [Event<'d>; EVENT_COUNT], tasks: [Task<'d>; TASK_COUNT]) -> Self {\n        let val = DPPI_ENABLE_BIT | (ch.number() as u32 & DPPI_CHANNEL_MASK);\n        for task in tasks {\n            if unsafe { task.subscribe_reg().read_volatile() } != 0 {\n                panic!(\"Task is already in use\");\n            }\n            unsafe { task.subscribe_reg().write_volatile(val) }\n        }\n        for event in events {\n            if unsafe { event.publish_reg().read_volatile() } != 0 {\n                panic!(\"Event is already in use\");\n            }\n            unsafe { event.publish_reg().write_volatile(val) }\n        }\n\n        Self { ch, events, tasks }\n    }\n}\n\nimpl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'d, C, EVENT_COUNT, TASK_COUNT> {\n    /// Enables the channel.\n    pub fn enable(&mut self) {\n        let n = self.ch.number();","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/embassy-rs/embassy/blob/463a07b963419a1bfe61d5d597c44acb810afb8b/embassy-nrf/src/ppi/dppi.rs#L16-L52","documentation":"new_many_to_many panics when a task's SUBSCRIBE register already holds a non-zero value, meaning another PPI/DPPI channel is already subscribed to that task. This guard prevents silently reconfiguring hardware task routing. The driver checks the register with a volatile read before writing the new channel configuration.","triggerScenarios":"Calling dppi::ManyToMany::new_many_to_many(ch, events, tasks) with a Task whose SUBSCRIBE register is non-zero — i.e. the task was already claimed by a previously configured PPI channel (via new_one_to_one, new_many_to_many, or manual register writes) that is still enabled or not torn down.","commonSituations":"Configuring two PPI channels that both drive the same timer task (e.g. two CLEAR/START task connections); calling new_many_to_many twice with an overlapping task; dropping a previous channel struct without disabling the DPPI channel so registers stay non-zero; reconfiguring after a soft reset without clearing SUBSCRIBE registers.","solutions":["Drop or tear down the existing PPI channel that subscribes to the same task before calling new_many_to_many again.","Audit the task list for duplicates — ensure no task appears in two configurations at once.","If reconfiguring is intentional, disable the old DPPI channel and zero its SUBSCRIBE registers first.","Restructure the application so one channel handles all events->task combinations for a shared task."],"exampleFix":"// before\nlet c1 = ManyToMany::new_many_to_many(p.PPI_CH0, [timer_event], [timer_task_clear]);\nlet c2 = ManyToMany::new_many_to_many(p.PPI_CH1, [other_event], [timer_task_clear]); // panics\n// after\nlet c1 = ManyToMany::new_many_to_many(p.PPI_CH0, [timer_event, other_event], [timer_task_clear]);\ndrop(c1); // if a rebuild is truly needed, drop before recreating","handlingStrategy":"validation","validationCode":"// Track claimed tasks in application state; check before configuring.\nlet task_reg = task.subscribe_reg().read_volatile();\nif task_reg != 0 {\n    // task already subscribed: reuse existing channel or tear it down first\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep one owner module for all PPI channel construction so task/event claims are centralized.","Drop channel wrappers explicitly before reconfiguration.","Never build two channel structs referencing the same Task peripheral value."],"tags":["embedded","nrf","ppi","hardware-conflict","panic"],"backgroundTag":"invalid-state-transition","analyzedSha":"463a07b963419a1bfe61d5d597c44acb810afb8b","analyzedAt":"2026-09-10T13:38:26.660Z","contentChangedAt":"2026-09-10T13:38:26.660Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}