pxl_rust/runtime/
map.rs

1//! Abstracts data-parallel computation based on a map operation for a given job.
2//!
3//! `Map` module enables efficient execution of the same device kernel across
4//! multiple chunks of input data in parallel. It is designed to work within a job
5//! context, leveraging hardware-level parallelism in XCENA device.
6//!
7//! It applies the same device kernel across each chunk of the input data,
8//! leveraging hardware-level parallelism for performance.
9//!
10//! This module provides a Rust abstraction around the C++ `pxl::runtime::Map` class.
11use crate::ffi::ArgInfo_t;
12use crate::ffi::{self, PxlResult};
13use crate::runtime::Stream;
14use std::ffi::c_void;
15use std::pin::Pin;
16
17/// Abstracts data-parallel computation based on a map operation for a given job.
18///
19/// This struct wraps a FFI pointer to the C++ `pxl::runtime::Map` class.
20/// It provides methods for executing parallel processing, synchronizing operations,
21/// and setting up asynchronous callbacks and stream.
22pub struct Map {
23    /// Pointer to the underlying FFI `Map` object.
24    inner: *mut ffi::Map,
25    /// Pointer to the underlying FFI `Stream` object.
26    stream_: *mut ffi::Stream,
27}
28
29/// Implementation of the `Map` struct.
30impl Map {
31    /// Creates a new `Map` wrapper from a raw FFI pointer.
32    /// This is called in runtime wrapper APIs requiring an actual `Map` object.
33    /// # Arguments
34    /// * `ptr` - A raw pointer to the underlying FFI `Map` object.
35    /// # Safety
36    /// Caller must ensure that `ptr` is valid and remains valid for the lifetime of `Map`.
37    pub(crate) fn new(ptr: *mut ffi::Map) -> Self {
38        Map {
39            inner: ptr,
40            stream_: unsafe { ffi::createStream() },
41        }
42    }
43
44    pub(crate) fn destroy(&mut self) {
45        if !self.inner.is_null() {
46            unsafe { ffi::destroy_map(self.inner) };
47            unsafe { ffi::destroyStream(self.stream_) };
48            self.inner = std::ptr::null_mut();
49            self.stream_ = std::ptr::null_mut();
50        }
51    }
52
53    /// gets the raw pointer of the underlying FFI `Map` object.
54    pub fn get(&self) -> *mut ffi::Map {
55        self.inner
56    }
57
58    /// Sets success callback function with user data.
59    /// # Arguments
60    /// * `callback` - Callback function as a c_void pointer.
61    /// * `user_data` - User data as a c_void pointer.
62    /// # Safety
63    /// Caller must ensure that the callback, user data remain valid for the lifetime of `Map`.
64    /// # Example
65    /// ```
66    /// use std::ffi::c_void;
67    /// use std::sync::{
68    ///     atomic::{AtomicBool, Ordering},
69    ///     Arc,
70    /// };    ///
71    /// unsafe extern "C" fn completion_callback(ptr: *mut c_void) {
72    ///     let flag = ptr as *const AtomicBool;
73    ///     println!("✅ Completion callback called with ptr: {:?}", flag);
74    ///     (*flag).store(true, Ordering::SeqCst);
75    /// }
76    /// let done_flag = Arc::new(AtomicBool::new(false));
77    /// let done_ptr = Arc::into_raw(done_flag.clone()) as *mut c_void;
78    /// map.set_completion_callback(completion_callback as *mut c_void, done_ptr);
79    /// ```
80    pub fn set_completion_callback(&mut self, callback: *mut c_void, user_data: *mut c_void) {
81        unsafe {
82            ffi::map_set_completion_callback(
83                self.inner,
84                callback as *mut ffi::c_void,
85                user_data as *mut ffi::c_void,
86            );
87        }
88    }
89
90    /// Sets error callback function with user data.
91    /// # Arguments
92    /// * `callback` - Callback function as a `c_void` pointer.
93    /// * `user_data` - User data as a `c_void` pointer.
94    /// # Safety
95    /// Caller must ensure that the callback and user data remain valid for the lifetime of `Map`.
96    /// # Example
97    /// ```
98    /// use std::ffi::c_void;
99    /// use std::sync::{
100    ///     atomic::{AtomicBool, Ordering},
101    ///     Arc,
102    /// };
103    /// unsafe extern "C" fn error_callback(ptr: *mut c_void) {
104    ///     let flag = ptr as *const AtomicBool;
105    ///     println!("❌ Error callback called with ptr: {:?}", flag);
106    ///     (*flag).store(true, Ordering::SeqCst);
107    /// }
108    /// let error_flag = Arc::new(AtomicBool::new(false));
109    /// let error_ptr = Arc::into_raw(error_flag.clone()) as *mut c_void;
110    /// map.set_error_callback(error_callback as *mut c_void, error_ptr);
111    /// ```
112    pub fn set_error_callback(&mut self, callback: *mut c_void, user_data: *mut c_void) {
113        unsafe {
114            ffi::map_set_error_callback(
115                self.inner,
116                callback as *mut ffi::c_void,
117                user_data as *mut ffi::c_void,
118            );
119        }
120    }
121
122    /// Sets message callback function with user data.
123    /// # Arguments
124    /// * `callback` - Callback function as a `c_void` pointer.
125    /// * `user_data` - User data as a `c_void` pointer.
126    /// # Safety
127    /// Caller must ensure that the callback and user data remain valid for the lifetime of `Map`.
128    /// # Example
129    /// ```
130    /// use std::ffi::c_void;
131    /// use std::sync::Arc;
132    /// unsafe extern "C" fn message_callback(ptr: *mut c_void) {
133    ///     println!("📩 Message callback called with ptr: {:?}", ptr);
134    /// }
135    /// let message_data = Arc::new(42); // Example user data
136    /// let message_ptr = Arc::into_raw(message_data.clone()) as *mut c_void;
137    /// map.set_message_callback(message_callback as *mut c_void, message_ptr);
138    /// ```
139    pub fn set_message_callback(&mut self, callback: *mut c_void, user_data: *mut c_void) {
140        unsafe {
141            ffi::map_set_message_callback(
142                self.inner,
143                callback as *mut ffi::c_void,
144                user_data as *mut ffi::c_void,
145            );
146        }
147    }
148
149    pub fn set_batch_size(&mut self, batch_size: &u32) {
150        unsafe {
151            let inner_pin = Pin::new_unchecked(&mut *self.inner);
152            inner_pin.setBatchSize(batch_size);
153        }
154    }
155
156    /// Sets the cluster bitmap for the map operation.
157    /// # Arguments
158    /// * `cluster_bitmap` - The cluster bitmap to be used for the map operation (default = 0xF).
159    /// # Example
160    /// ```
161    /// map.set_cluster_bitmap(0xF); // Use all clusters
162    /// ```
163    pub fn set_cluster_bitmap(&mut self, cluster_bitmap: u32) {
164        unsafe {
165            let inner_pin = Pin::new_unchecked(&mut *self.inner);
166            inner_pin.setClusterBitmap(&cluster_bitmap);
167        }
168    }
169
170    /// Sets the stream to the default stream.
171    /// # Example
172    /// ```
173    /// map.set_default_stream()?;
174    /// ```
175    pub fn set_default_stream(&mut self) {
176        unsafe {
177            ffi::map_set_default_stream(self.inner);
178        }
179    }
180
181    /// Sets the `Stream` for `Map` object.
182    /// # Arguments
183    /// * `stream` - A reference to the `Stream` object to be set.
184    /// # Example
185    /// ```rust
186    /// let stream = pxl::runtime::create_stream();
187    /// map.set_stream(&stream);
188    /// ```
189    pub fn set_stream(&mut self, stream: &Stream) {
190        self.stream_ = stream.get();
191    }
192
193    /// Returns the stream ID that the map operation is associated with.
194    /// # Returns
195    /// The stream ID as a `u32`.
196    /// # Example
197    /// ```
198    /// let stream_id = map.stream_id();
199    /// println!("Current stream ID: {}", stream_id);
200    /// ```
201    pub fn stream_id(&self) -> u32 {
202        unsafe { (*self.inner).streamId() }
203    }
204
205    /// Executes with the given arguments.
206    /// This is called in execute! macro.
207    /// # Arguments
208    /// * `args` - A vector of `ArgInfo_t` objects representing the arguments to be passed.
209    /// # Returns
210    /// Execution status as a PxlResult.
211    /// # Safety
212    /// Caller must ensure that the `args` vector is valid and contains valid pointers.
213    /// # Example
214    /// Please refer to usage of `execute!` macro.
215    pub fn execute(&mut self, args: &Vec<ArgInfo_t>) -> PxlResult {
216        unsafe {
217            // println!("Executing with arguments:");
218            // for arg in args.iter() {
219            //     println!("{:?}", arg);
220            // }
221            let args_ptr = args.as_ptr() as *mut ArgInfo_t;
222            let num_args = args.len();
223            ffi::map_execute(self.inner, args_ptr, num_args)
224        }
225    }
226
227    /// Synchronizes the `Map` operation after execute request.
228    /// # Returns
229    /// Synchronization status as a PxlResult.
230    /// # Safety
231    /// Caller must ensure that execution status returned from `execute` is valid.
232    /// # Example
233    /// ```rust
234    /// let ret = map.synchronize();
235    /// ```
236    pub fn synchronize(&mut self) -> PxlResult {
237        unsafe { Pin::new_unchecked(&mut *self.inner).synchronize() }
238    }
239
240    /// Gets the execution status of the map operation.
241    /// # Returns
242    /// `ExecuteStatus` indicating the current execution status.
243    /// # Example
244    /// ```
245    /// use pxl::ExecuteStatus;
246    /// let status = map.get_execute_status();
247    /// match status {
248    ///     ExecuteStatus::ExecuteDone => println!("Execution completed successfully"),
249    ///     ExecuteStatus::Fail => println!("Execution failed"),
250    ///     _ => println!("Execution in progress or other status"),
251    /// }
252    /// ```
253    pub fn get_execute_status(&self) -> ffi::ExecuteStatus {
254        unsafe { (*self.inner).getExecuteStatus() }
255    }
256
257    /// Gets the error report of the map operation.
258    /// # Returns
259    /// `Vec<KernelError_t>` containing the error report.
260    /// If successful, the beginIndex and endIndex will be 0xFFFFFFFF.
261    /// If unsuccessful, they indicate the range of failed task index.
262    /// # Example
263    /// ```
264    /// let kernel_errors = map.get_kernel_error();
265    /// for error in &kernel_errors {
266    ///     if error.beginIndex != 0xFFFFFFFF && error.endIndex != 0xFFFFFFFF {
267    ///         println!("Error in range: {} - {}", error.beginIndex, error.endIndex);
268    ///     }
269    /// }
270    /// ```
271    pub fn get_kernel_error(&mut self) -> Vec<ffi::KernelError_t> {
272        unsafe {
273            let mut error_count: usize = 0;
274            let error_ptr = ffi::map_get_kernel_error(self.inner, &mut error_count as *mut usize);
275
276            if error_ptr.is_null() || error_count == 0 {
277                return Vec::new();
278            }
279
280            let errors = std::slice::from_raw_parts(error_ptr, error_count);
281            errors.to_vec()
282        }
283    }
284}