binaryninja/
background_task.rs

1// Copyright 2021-2026 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Background tasks provide plugins the ability to inform the core of long-running background tasks.
16
17use binaryninjacore_sys::*;
18use std::fmt::Debug;
19
20use std::result;
21
22use crate::rc::*;
23use crate::string::*;
24
25pub type Result<R> = result::Result<R, ()>;
26
27/// An RAII guard for [`BackgroundTask`] to finish the task when dropped.
28pub struct OwnedBackgroundTaskGuard {
29    pub(crate) task: Ref<BackgroundTask>,
30}
31
32impl OwnedBackgroundTaskGuard {
33    pub fn cancel(&self) {
34        self.task.cancel();
35    }
36
37    pub fn is_cancelled(&self) -> bool {
38        self.task.is_cancelled()
39    }
40
41    pub fn set_progress_text(&self, text: &str) {
42        self.task.set_progress_text(text);
43    }
44}
45
46impl Drop for OwnedBackgroundTaskGuard {
47    fn drop(&mut self) {
48        self.task.finish();
49    }
50}
51
52/// A [`BackgroundTask`] does not actually execute any code, only act as a handler, primarily to query
53/// the status of the task, and to cancel the task.
54///
55/// If you are looking to execute code in the background, consider using rusts threading API, or if you
56/// want the core to execute the task on a worker thread, instead use the [`crate::worker_thread`] API.
57///
58/// NOTE: If you do not call [`BackgroundTask::finish`] or [`BackgroundTask::cancel`], the task will
59/// persist even _after_ it has been dropped, use [`OwnedBackgroundTaskGuard`] to ensure the task is
60/// finished, see [`BackgroundTask::enter`] for usage.
61#[derive(PartialEq, Eq, Hash)]
62pub struct BackgroundTask {
63    pub(crate) handle: *mut BNBackgroundTask,
64}
65
66impl BackgroundTask {
67    pub(crate) unsafe fn from_raw(handle: *mut BNBackgroundTask) -> Self {
68        debug_assert!(!handle.is_null());
69
70        Self { handle }
71    }
72
73    pub fn new(initial_text: &str, can_cancel: bool) -> Ref<Self> {
74        let text = initial_text.to_cstr();
75        let handle = unsafe { BNBeginBackgroundTask(text.as_ptr(), can_cancel) };
76        // We should always be returned a valid task.
77        assert!(!handle.is_null());
78        unsafe { Ref::new(Self { handle }) }
79    }
80
81    /// Creates a [`OwnedBackgroundTaskGuard`] that is responsible for finishing the background task
82    /// once dropped. Because the status of a task does not dictate the underlying objects' lifetime,
83    /// this can be safely done without requiring exclusive ownership.
84    pub fn enter(&self) -> OwnedBackgroundTaskGuard {
85        OwnedBackgroundTaskGuard {
86            task: self.to_owned(),
87        }
88    }
89
90    pub fn can_cancel(&self) -> bool {
91        unsafe { BNCanCancelBackgroundTask(self.handle) }
92    }
93
94    pub fn is_cancelled(&self) -> bool {
95        unsafe { BNIsBackgroundTaskCancelled(self.handle) }
96    }
97
98    pub fn cancel(&self) {
99        unsafe { BNCancelBackgroundTask(self.handle) }
100    }
101
102    pub fn is_finished(&self) -> bool {
103        unsafe { BNIsBackgroundTaskFinished(self.handle) }
104    }
105
106    pub fn finish(&self) {
107        unsafe { BNFinishBackgroundTask(self.handle) }
108    }
109
110    pub fn progress_text(&self) -> String {
111        unsafe { BnString::into_string(BNGetBackgroundTaskProgressText(self.handle)) }
112    }
113
114    pub fn set_progress_text(&self, text: &str) {
115        let progress_text = text.to_cstr();
116        unsafe { BNSetBackgroundTaskProgressText(self.handle, progress_text.as_ptr()) }
117    }
118
119    pub fn running_tasks() -> Array<BackgroundTask> {
120        unsafe {
121            let mut count = 0;
122            let handles = BNGetRunningBackgroundTasks(&mut count);
123            Array::new(handles, count, ())
124        }
125    }
126}
127
128impl Debug for BackgroundTask {
129    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130        f.debug_struct("BackgroundTask")
131            .field("progress_text", &self.progress_text())
132            .field("can_cancel", &self.can_cancel())
133            .field("is_cancelled", &self.is_cancelled())
134            .field("is_finished", &self.is_finished())
135            .finish()
136    }
137}
138
139unsafe impl RefCountable for BackgroundTask {
140    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
141        Ref::new(Self {
142            handle: BNNewBackgroundTaskReference(handle.handle),
143        })
144    }
145
146    unsafe fn dec_ref(handle: &Self) {
147        BNFreeBackgroundTask(handle.handle);
148    }
149}
150
151impl CoreArrayProvider for BackgroundTask {
152    type Raw = *mut BNBackgroundTask;
153    type Context = ();
154    type Wrapped<'a> = Guard<'a, BackgroundTask>;
155}
156
157unsafe impl CoreArrayProviderInner for BackgroundTask {
158    unsafe fn free(raw: *mut *mut BNBackgroundTask, count: usize, _context: &()) {
159        BNFreeBackgroundTaskList(raw, count);
160    }
161    unsafe fn wrap_raw<'a>(raw: &'a *mut BNBackgroundTask, context: &'a ()) -> Self::Wrapped<'a> {
162        Guard::new(BackgroundTask::from_raw(*raw), context)
163    }
164}
165
166impl ToOwned for BackgroundTask {
167    type Owned = Ref<Self>;
168
169    fn to_owned(&self) -> Self::Owned {
170        unsafe { RefCountable::inc_ref(self) }
171    }
172}
173
174unsafe impl Send for BackgroundTask {}
175unsafe impl Sync for BackgroundTask {}