mcl_rs/
task.rs

1#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
2use crate::device::DevType;
3use crate::low_level;
4use crate::low_level::{ArgOpt, ReqStatus, TaskOpt};
5use crate::registered_buffer::RegisteredBuffer;
6#[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
7use crate::registered_buffer::SharedMemBuffer;
8use libmcl_sys::*;
9#[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
10use std::os::unix::raw::pid_t;
11
12unsafe impl Send for Task<'_> {}
13unsafe impl Sync for Task<'_> {}
14
15/// Represents an MCL task whose kernel has been set but the arguments and target device are missing
16pub struct Task<'a> {
17    // we want to stare the actual reference to the original argument so we can track lifetimes appropriately
18    // this also will allow us to protect agains the same argument being used as an output simulataneous
19    // aka this prevents us from doing dirty C-memory things :)
20    args: Vec<TaskArgOrBuf<'a>>,
21    curr_arg: usize,
22    les: Option<[u64; 3]>,
23    dev: DevType,
24    c_handle: *mut mcl_handle,
25    #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
26    shared_id: Option<u32>,
27}
28
29impl<'a> Task<'a> {
30    pub(crate) fn new(kernel_name_cl: &str, nargs: usize, flags: TaskOpt) -> Self {
31        let c_handle = low_level::task_create(flags);
32        #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
33        let mut id = None;
34        #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
35        if flags.contains(TaskOpt::SHARED) {
36            id = low_level::get_shared_task_id(c_handle)
37        }
38        let task = Task {
39            args: vec![Default::default(); nargs],
40            curr_arg: 0,
41            les: None,
42            dev: DevType::ANY,
43            c_handle: c_handle,
44            #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
45            shared_id: id,
46        };
47        low_level::task_set_kernel(task.c_handle, kernel_name_cl, nargs as u64);
48        task
49    }
50
51    /// Set a new argument `arg` for `this` mcl task.
52    /// Note the order in which tasks are set must match the order expected by the kernel
53    ///
54    /// Returns the Task with the arg set
55    ///
56    /// # Examples
57    ///```no_run
58    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
59    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
60    ///         .with_compile_args("-D MYDEF").load();
61    ///     
62    ///     let data = vec![0; 4];
63    ///     let mut out = vec![0; 4];
64    ///     let pes: [u64; 3] = [1, 1, 1];
65    ///
66    ///     let mcl_future = mcl.task("my_kernel", 1)
67    ///                 .arg(mcl_rs::TaskArg::input_slice(&data)) //the first argument to the kernel is  the input array
68    ///                 .arg(mcl_rs::TaskArg::output_slice(&mut out))//the second argument to the kernel is  the output array
69    ///                 .exec(pes);
70    ///     futures::executor::block_on(mcl_future);
71    ///
72    ///```
73    pub fn arg(mut self, arg: TaskArg<'a>) -> Self {
74        match &arg.data {
75            TaskArgData::Scalar(x) => {
76                low_level::task_set_arg(self.c_handle, self.curr_arg as u64, x, arg.flags)
77            }
78            TaskArgData::Buffer(x) => {
79                low_level::task_set_arg(self.c_handle, self.curr_arg as u64, x, arg.flags)
80            }
81            // TaskArgData::Local(x) => {
82            //     low_level::task_set_local(self.c_handle, self.curr_arg as u64, *x, arg.flags)
83            // }
84            #[cfg(feature = "shared_mem")]
85            TaskArgData::Shared(..) => panic!("must use arg_shared_buffer api "),
86            TaskArgData::Empty => panic!("cannot have an empty arg"),
87        }
88        self.args[self.curr_arg] = TaskArgOrBuf::TaskArg(arg);
89        self.curr_arg += 1;
90
91        self
92    }
93
94    /// Set a new argument registered buffer `arg` for `this` mcl task in preparation
95    ///
96    /// Returns the Task with the arg set
97    ///
98    /// # Examples
99    ///```no_run
100    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
101    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
102    ///         .with_compile_args("-D MYDEF").load();
103    ///     
104    ///     let mut a = vec![0;100];
105    ///     let buf = mcl.register_buffer(mcl_rs::TaskArg::inout_slice(&mut a)
106    ///            .resident(true)
107    ///            .dynamic(true),
108    ///     );
109    ///     let pes: [u64; 3] = [1, 1, 1];
110    ///
111    ///     let mcl_future = mcl.task("my_kernel", 1)
112    ///                 .arg_buffer(buf)
113    ///                 .exec(pes);
114    ///     futures::executor::block_on(mcl_future);
115    ///
116    ///```
117    pub fn arg_buffer(mut self, buffer: RegisteredBuffer<'a>) -> Self {
118        low_level::task_set_arg_registered_buffer(self.c_handle, self.curr_arg as u64, &buffer);
119        self.args[self.curr_arg] = TaskArgOrBuf::RegBuf(buffer.clone());
120        self.curr_arg += 1;
121
122        self
123    }
124
125    /// Set a new shared argument buffer `arg` for `this` mcl task in preparation
126    ///
127    /// Returns the Task with the arg set
128    ///
129    /// # Saftey
130    ///
131    /// We track the reader and writers associated with this buffer to protect access, but that only applies to this process
132    ///
133    /// Regardless of and local saftey guarentees we provide, using a shared buffer is still inherently unsafe as this represents a multi-processed shared memory buffer, managed by the MCL c-library, no guarantees can be provided on
134    /// multiple processes modifying this buffer simultaneously
135    ///
136    /// # Examples
137    ///```no_run
138    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
139    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
140    ///         .with_compile_args("-D MYDEF").load();
141    ///     
142    ///     let num_elems = 100;
143    ///     let buf = mcl.create_shared_buffer(mcl_rs::TaskArg::inout_shared::<u32>("my_buffer", num_elems));
144    ///     let pes: [u64; 3] = [1, 1, 1];
145    ///
146    ///     let mcl_future = unsafe {
147    ///         mcl.task("my_kernel", 1)
148    ///             .arg_shared(buf) //potentially accessed by other proccesses hence unsafe
149    ///             .exec(pes)
150    ///     };
151    ///     futures::executor::block_on(mcl_future);
152    ///
153    ///```
154    #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
155    pub unsafe fn arg_shared_buffer(mut self, buffer: SharedMemBuffer) -> Self {
156        //TODO fix the offset issue
157        low_level::task_set_arg_shared_mem_buffer(self.c_handle, self.curr_arg as u64, &buffer);
158        self.args[self.curr_arg] = TaskArgOrBuf::ShmBuf(buffer.clone());
159        self.curr_arg += 1;
160
161        self
162    }
163
164    /// Set the local workgroup size `les` to the mcl task in preparation
165    ///
166    ///
167    /// Returns the Task with the local workgroup size  set
168    ///
169    /// # Examples
170    ///    
171    ///```no_run
172    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
173    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
174    ///         .with_compile_args("-D MYDEF").load();
175    ///     let data = vec![0; 4];
176    ///     let les: [u64; 3] = [1, 1, 1];
177    ///     let pes: [u64; 3] = [1, 1, 1];
178    ///     let mcl_future = mcl.task("my_kernel", 1)
179    ///                 .arg(mcl_rs::TaskArg::input_slice(&data))
180    ///                 .lwsize(les)
181    ///                 .exec(pes);
182    ///     futures::executor::block_on(mcl_future);
183    ///```
184    pub fn lwsize(mut self, les: [u64; 3]) -> Self {
185        self.les = Some(les);
186
187        return self;
188    }
189
190    /// Set the preferred device `dev` to the mcl task in preparation
191    ///
192    /// Returns the Task with the desired device  set
193    ///
194    /// # Examples
195    ///     
196    ///```no_run
197    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
198    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
199    ///         .with_compile_args("-D MYDEF").load();
200    ///     let data = vec![0; 4];
201    ///     let les: [u64; 3] = [1, 1, 1];
202    ///     let pes: [u64; 3] = [1, 1, 1];
203    ///     let mcl_future = mcl.task("my_kernel", 1)
204    ///                 .arg(mcl_rs::TaskArg::input_slice(&data))
205    ///                 .lwsize(les)
206    ///                 .dev(mcl_rs::DevType::CPU)
207    ///                 .exec(pes);
208    ///     futures::executor::block_on(mcl_future);
209    ///```
210    pub fn dev(mut self, dev: DevType) -> Self {
211        self.dev = dev;
212
213        return self;
214    }
215
216    /// If this is a shared task return the task id otherwise return None
217    ///
218    /// # Examples
219    ///```no_run
220    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
221    ///     mcl.load_prog("my_prog",mcl_rs::PrgType::Src);
222    ///     let data = vec![0; 4];
223    ///     let pes: [u64; 3] = [1, 1, 1];
224    ///
225    ///     let task = mcl.task("my_kernel", 1);
226    ///     assert_eq!(task.shared_id(), None);
227    ///
228    ///     let shared_task = mcl.shared_task("my_kernel2", 1);
229    ///     assert!(shared_task.shared_id().is_some());
230    ///```
231    #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
232    pub fn shared_id(&self) -> Option<u32> {
233        self.shared_id
234    }
235
236    /// Submit the task for execution
237    /// This is an asynchronous operation, meaning that no work is actually performed until
238    /// the returned future is actualy `await`ed.
239    /// While awaiting a task execution, the user application will not make forward progress until
240    /// the underylying device has executed the task and performed any necessary data transfers.
241    /// Upon return from the await call, any output data is gauranteed to be written to its approriate buffer.
242    ///
243    /// Task execution order can be enforced by sequentially awaiting tasks, or may be executed simultaneously
244    /// using data structures such as Join_all <https://docs.rs/futures/latest/futures/future/fn.join_all.html>
245    /// # Examples
246    ///     
247    ///```no_run
248    ///     let mcl = mcl_rs::MclEnvBuilder::new().num_workers(10).initialize();
249    ///     mcl.load_prog("my_path", mcl_rs::PrgType::Src);
250    ///     let data = vec![0; 4];
251    ///     let pes: [u64; 3] = [1, 1, 1];
252    ///     let task_1 = mcl.task("my_kernel", 1)
253    ///                 .arg(mcl_rs::TaskArg::input_slice(&data))
254    ///                 .dev(mcl_rs::DevType::CPU)
255    ///                 .exec(pes); //this creates a future we need to await
256    ///     let task_2 = mcl.task("my_kernel", 1)
257    ///                 .arg(mcl_rs::TaskArg::input_slice(&data))
258    ///                 .dev(mcl_rs::DevType::CPU)
259    ///                 .exec(pes); //this creates a future we need to await
260    ///     let sequential_tasks = async move{
261    ///         task_1.await; //task will execute before task 2 is even submitted
262    ///         task_2.await;
263    ///     };
264    ///     futures::executor::block_on(sequential_tasks);
265    ///     
266    ///     let task_3 = mcl.task("my_kernel", 1)
267    ///                 .arg(mcl_rs::TaskArg::input_slice(&data))
268    ///                 .dev(mcl_rs::DevType::CPU)
269    ///                 .exec(pes); //this creates a future we need to await
270    ///     let task_4 = mcl.task("my_kernel", 1)
271    ///                 .arg(mcl_rs::TaskArg::input_slice(&data))
272    ///                 .dev(mcl_rs::DevType::CPU)
273    ///                 .exec(pes); //this creates a future we need to await
274    ///     let simultaneous_tasks = futures::future::join_all([task_3,task_4]);
275    ///     futures::executor::block_on(simultaneous_tasks); //both tasks submitted "simultaneously"
276    ///```
277    pub async fn exec(mut self, ref mut pes: [u64; 3]) {
278        assert_eq!(self.curr_arg, self.args.len());
279        for arg in &self.args {
280            match arg {
281                TaskArgOrBuf::RegBuf(buf) => buf.alloc().await,
282                #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
283                TaskArgOrBuf::ShmBuf(buf) => buf.alloc().await,
284                TaskArgOrBuf::TaskArg(_) => {}
285            }
286        }
287
288        low_level::exec(self.c_handle, pes, &mut self.les, self.dev);
289
290        while low_level::test(self.c_handle) != ReqStatus::Completed {
291            async_std::task::yield_now().await;
292        }
293    }
294}
295
296impl Drop for Task<'_> {
297    fn drop(&mut self) {
298        low_level::task_free(self.c_handle);
299    }
300}
301
302#[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
303pub struct SharedTask {
304    pid: pid_t,
305    hdl_id: u32,
306}
307
308/// Represents a shared task handle, that can be use to await the completion of a task on a different process from the one which created it
309#[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
310impl SharedTask {
311    pub(crate) fn new(pid: pid_t, hdl_id: u32) -> SharedTask {
312        SharedTask { pid, hdl_id }
313    }
314
315    /// Await the completetion of the shared task.
316    /// This is an asynchronous operation, meaning that no work is actually performed until
317    /// the returned future is actualy `await`ed.
318    /// While awaiting a task execution, the user application will not make forward progress until
319    /// the underylying device has executed the task and performed any necessary data transfers.
320    /// Upon return from the await call, any output data is gauranteed to be written to its approriate buffer.
321    ///
322    /// Task execution order can be enforced be sequentially awaiting tasks, or may be executed simultaneous
323    /// using data structures such as Join_all <https://docs.rs/futures/latest/futures/future/fn.join_all.html>
324    /// # Examples
325    ///     
326    ///```no_run
327    ///     let mcl = mcl_rs::MclEnvBuilder::new().num_workers(10).initialize();
328    ///     mcl.load_prog("my_path", mcl_rs::PrgType::Src);
329    ///     let pid = 0; //user is required to set this approriately
330    ///     let task_ids = [0,1,2,3]; //user is required to set this approriately
331    ///
332    ///     let t1 = mcl.attach_shared_task(pid,task_ids[0]);
333    ///     let t2 = mcl.attach_shared_task(pid,task_ids[1]);
334    ///     let t3 = mcl.attach_shared_task(pid,task_ids[2]);
335    ///     let t4 = mcl.attach_shared_task(pid,task_ids[3]);
336    ///     
337    ///     let tasks = async move {
338    ///         t1.await;
339    ///         t2.await;
340    ///         futures::future::join_all([t3,t4]).await;
341    ///     }
342    ///     futures::executor::block_on(tasks);
343    ///```
344    pub async fn wait(&self) {
345        while low_level::shared_task_test(self.pid, self.hdl_id) != ReqStatus::Completed {
346            async_std::task::yield_now().await;
347        }
348    }
349}
350
351#[derive(Clone)]
352enum TaskArgOrBuf<'a> {
353    TaskArg(TaskArg<'a>),
354    RegBuf(RegisteredBuffer<'a>),
355    #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
356    ShmBuf(SharedMemBuffer),
357}
358
359impl<'a> Default for TaskArgOrBuf<'a> {
360    fn default() -> Self {
361        TaskArgOrBuf::TaskArg(Default::default())
362    }
363}
364
365#[derive(Clone)]
366pub(crate) enum TaskArgData<'a> {
367    Scalar(&'a [u8]),
368    Buffer(&'a [u8]),
369    // Local(usize),
370    #[cfg(feature = "shared_mem")]
371    Shared(String, usize),
372    Empty,
373}
374
375impl<'a> TaskArgData<'a> {
376    pub(crate) fn len(&self) -> usize {
377        match self {
378            TaskArgData::Scalar(x) => x.len(),
379            TaskArgData::Buffer(x) => x.len(),
380            // TaskArgData::Local(x) => *x,
381            #[cfg(feature = "shared_mem")]
382            TaskArgData::Shared(_, x) => *x,
383            TaskArgData::Empty => 0,
384        }
385    }
386}
387
388/// Represents a data argument for an MCL task along with the use flags (e.g. input, output, access type etc.)
389#[derive(Clone)]
390pub struct TaskArg<'a> {
391    pub(crate) data: TaskArgData<'a>,
392    pub(crate) flags: ArgOpt,
393    pub(crate) orig_type_size: usize,
394}
395
396impl<'a> Default for TaskArg<'a> {
397    fn default() -> Self {
398        TaskArg {
399            data: TaskArgData::Empty,
400            flags: ArgOpt::EMPTY,
401            orig_type_size: 0,
402        }
403    }
404}
405
406fn to_u8_slice<T>(data: &[T]) -> &[u8] {
407    let num_bytes = std::mem::size_of::<T>() * data.len();
408    unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, num_bytes) }
409    //no alignment issues going from T to u8 as u8 aligns to everything
410}
411
412impl<'a> TaskArg<'a> {
413    /// Create a new task input argument from `slice`
414    ///
415    ///
416    /// # Examples
417    ///     
418    ///```no_run
419    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
420    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
421    ///         .with_compile_args("-D MYDEF").load();
422    ///     let data = vec![0; 4];
423    ///     let task = mcl.task("my_kernel", 1)
424    ///                 .arg(mcl_rs::TaskArg::input_slice(&data));
425    ///```
426    // pub fn input_slice<T: Into<MclBufferDataType<'a>>>(slice: T) -> Self {
427    pub fn input_slice<T>(slice: &'a [T]) -> Self {
428        TaskArg {
429            data: TaskArgData::Buffer(to_u8_slice(slice)),
430            flags: ArgOpt::INPUT | ArgOpt::BUFFER,
431            orig_type_size: std::mem::size_of::<T>(),
432        }
433    }
434
435    /// Create a new task input argument from `scalar`
436    ///
437    ///
438    /// # Examples
439    ///     
440    ///```no_run
441    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
442    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
443    ///         .with_compile_args("-D MYDEF").load();
444    ///     let data = 4;
445    ///     let task = mcl.task("my_kernel", 1)
446    ///                 .arg(mcl_rs::TaskArg::input_scalar(&data));
447    ///```  
448    pub fn input_scalar<T>(scalar: &'a T) -> Self {
449        // let slice = std::
450        TaskArg {
451            data: TaskArgData::Scalar(to_u8_slice(std::slice::from_ref(scalar))),
452            flags: ArgOpt::INPUT | ArgOpt::SCALAR,
453            orig_type_size: std::mem::size_of::<T>(),
454        }
455    }
456    // /// Requests an allocation of `num_bytes`
457    // ///
458    // ///
459    // /// # Examples
460    // ///
461    // ///```no_run
462    // ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
463    // ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
464    // ///         .with_compile_args("-D MYDEF").load();
465    // ///     let data = 4;
466    // ///     let les: [u64; 3] = [1, 1, 1];
467    // ///     let pes: [u64; 3] = [1, 1, 1];
468    // ///     let task = mcl.task("my_kernel", 1)
469    // ///                 .arg(mcl_rs::TaskArg::input_local(400));
470    // ///```
471    // pub fn input_local(num_bytes: usize) -> Self {
472    //     TaskArg {
473    //         data: TaskArgData::Local(num_bytes),
474    //         flags: ArgOpt::LOCAL,
475    //         orig_type_size: 1,
476    //     }
477    // }
478
479    /// Create an new task input argument using a shared memory buffer
480    ///
481    /// # Examples
482    ///     
483    ///```no_run
484    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
485    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
486    ///         .with_compile_args("-D MYDEF").load();
487    ///
488    ///     let num_elems = 100;
489    ///     let buffer = mcl.create_shared_buffer(mcl_rs::TaskArg::input_shared::<f32>("my_buffer",num_elems).resident(true));
490    ///     let task = mcl.task("my_kernel",1)
491    ///                   .input_shared(buffer);
492    ///```  
493    #[cfg(feature = "shared_mem")]
494    pub fn input_shared<T>(name: &str, size: usize) -> Self {
495        TaskArg {
496            data: TaskArgData::Shared(name.to_owned(), size * std::mem::size_of::<T>()),
497            flags: ArgOpt::SHARED | ArgOpt::INPUT | ArgOpt::BUFFER,
498            orig_type_size: std::mem::size_of::<T>(),
499        }
500    }
501
502    /// Create a new task output argument from `slice`
503    ///
504    /// Returns a new TaskArg
505    ///
506    /// # Examples
507    ///     
508    ///```no_run
509    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
510    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
511    ///         .with_compile_args("-D MYDEF").load();
512    ///     let mut data = vec![0; 4];
513    ///     let mcl_future = mcl.task("my_kernel", 1)
514    ///                 .arg(mcl_rs::TaskArg::output_slice(&mut data));
515    ///```
516    pub fn output_slice<T>(slice: &'a mut [T]) -> Self {
517        TaskArg {
518            data: TaskArgData::Buffer(to_u8_slice(slice)),
519            flags: ArgOpt::OUTPUT | ArgOpt::BUFFER,
520            orig_type_size: std::mem::size_of::<T>(),
521        }
522    }
523
524    /// Create an new task output argument using a shared memory buffer
525    ///
526    /// # Examples
527    ///     
528    ///```no_run
529    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
530    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
531    ///         .with_compile_args("-D MYDEF").load();
532    ///
533    ///     let num_elems = 100;
534    ///     let buffer = mcl.create_shared_buffer(mcl_rs::TaskArg::output_shared::<f32>("my_buffer",num_elems).resident(true));
535    ///     let task = mcl.task("my_kernel",1)
536    ///                   .output_shared(buffer);
537    ///```  
538    #[cfg(feature = "shared_mem")]
539    pub fn output_shared<T>(name: &str, size: usize) -> Self {
540        TaskArg {
541            data: TaskArgData::Shared(name.to_owned(), size * std::mem::size_of::<T>()),
542            flags: ArgOpt::SHARED | ArgOpt::OUTPUT | ArgOpt::BUFFER,
543            orig_type_size: std::mem::size_of::<T>(),
544        }
545    }
546
547    /// Create a new task output argument from `scalar`
548    ///
549    /// Returns a new TaskArg
550    ///
551    /// # Examples
552    ///     
553    ///```no_run
554    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
555    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
556    ///         .with_compile_args("-D MYDEF").load();
557    ///     let mut data = 4;
558    ///     let mcl_future = mcl.task("my_kernel", 1)
559    ///                 .arg(mcl_rs::TaskArg::output_scalar(&mut data));
560    ///```
561    pub fn output_scalar<T>(scalar: &'a T) -> Self {
562        //mcl expects all outputs to be buffers but we want a nice consistent interface here!
563        TaskArg {
564            data: TaskArgData::Buffer(to_u8_slice(std::slice::from_ref(scalar))),
565            flags: ArgOpt::OUTPUT | ArgOpt::BUFFER,
566            orig_type_size: std::mem::size_of::<T>(),
567        }
568    }
569
570    /// Create a new task input+output argument from `slice`
571    ///
572    /// Returns a new TaskArg
573    ///
574    /// # Examples
575    ///     
576    ///```no_run
577    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
578    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
579    ///         .with_compile_args("-D MYDEF").load();
580    ///     let mut data = vec![0; 4];
581    ///     let mcl_future = mcl.task("my_kernel", 1)
582    ///                 .arg(mcl_rs::TaskArg::inout_slice(&mut data));
583    ///```
584    pub fn inout_slice<T>(slice: &'a [T]) -> Self {
585        TaskArg {
586            data: TaskArgData::Buffer(to_u8_slice(slice)),
587            flags: ArgOpt::OUTPUT | ArgOpt::INPUT | ArgOpt::BUFFER,
588            orig_type_size: std::mem::size_of::<T>(),
589        }
590    }
591
592    /// Create an new task input+output argument using a shared memory buffer
593    ///
594    /// # Examples
595    ///     
596    ///```no_run
597    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
598    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
599    ///         .with_compile_args("-D MYDEF").load();
600    ///
601    ///     let num_elems = 100;
602    ///     let buffer = mcl.create_shared_buffer(mcl_rs::TaskArg::inout_shared::<f32>("my_buffer",num_elems).resident(true));
603    ///     let task = mcl.task("my_kernel",1)
604    ///                   .inout_shared(buffer);
605    ///```  
606    #[cfg(feature = "shared_mem")]
607    pub fn inout_shared<T>(name: &str, size: usize) -> Self {
608        // println!("inout_shared: {size} {}",size*std::mem::size_of::<T>());
609        TaskArg {
610            data: TaskArgData::Shared(name.to_owned(), size * std::mem::size_of::<T>()),
611            flags: ArgOpt::SHARED | ArgOpt::INPUT | ArgOpt::OUTPUT | ArgOpt::BUFFER,
612            orig_type_size: std::mem::size_of::<T>(),
613        }
614    }
615
616    /// Create a new task input+output argument from `scalar`
617    ///
618    /// Returns a new TaskArg
619    ///     
620    /// # Examples
621    ///     
622    ///```no_run
623    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
624    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
625    ///         .with_compile_args("-D MYDEF").load();
626    ///     let mut data = 4;
627    ///     let mcl_future = mcl.task("my_kernel", 1)
628    ///                 .arg(mcl_rs::TaskArg::inout_scalar(&mut data));
629    ///```
630    pub fn inout_scalar<T>(scalar: &'a T) -> Self {
631        TaskArg {
632            data: TaskArgData::Buffer(to_u8_slice(std::slice::from_ref(scalar))),
633            flags: ArgOpt::OUTPUT | ArgOpt::INPUT | ArgOpt::BUFFER,
634            orig_type_size: std::mem::size_of::<T>(),
635        }
636    }
637
638    /// Sets the resident memory flag for the argument
639    ///
640    /// Returns the TaskArg with the preference set
641    ///
642    /// # Examples
643    ///     
644    ///```no_run
645    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
646    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
647    ///         .with_compile_args("-D MYDEF").load();
648    ///     let data = vec![0; 4];
649    ///     let les: [u64; 3] = [1, 1, 1];
650    ///     let pes: [u64; 3] = [1, 1, 1];
651    ///     let mcl_future = mcl.task("my_kernel", 1)
652    ///                 .arg(mcl_rs::TaskArg::input_slice(&data).resident(true))
653    ///                 .exec(pes);
654    ///     futures::executor::block_on(mcl_future);
655    ///```
656    pub fn resident(mut self, val: bool) -> Self {
657        if val {
658            self.flags = self.flags | ArgOpt::RESIDENT;
659        } else {
660            self.flags = self.flags & !ArgOpt::RESIDENT;
661        }
662        return self;
663    }
664
665    /// Sets the dynamic memory flag for the argument
666    ///
667    /// Returns the TaskArg with the preference set
668    ///
669    /// # Examples
670    ///     
671    ///```no_run
672    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
673    ///     mcl.load_prog("my_prog",mcl_rs::PrgType::Src);
674    ///     let data = vec![0; 4];
675    ///     let les: [u64; 3] = [1, 1, 1];
676    ///     let pes: [u64; 3] = [1, 1, 1];
677    ///     let mcl_future = mcl.task("my_kernel", 1)
678    ///                 .arg(mcl_rs::TaskArg::input_slice(&data).dynamic(true))
679    ///                 .exec(pes);
680    ///     futures::executor::block_on(mcl_future);
681    ///```
682    pub fn dynamic(mut self, val: bool) -> Self {
683        if val {
684            self.flags = self.flags | ArgOpt::DYNAMIC;
685        } else {
686            self.flags = self.flags & !ArgOpt::DYNAMIC;
687        }
688        return self;
689    }
690
691    /// Sets the done flag for the argument
692    ///
693    /// Returns the TaskArg with the preference set
694    ///
695    /// # Examples
696    ///     
697    ///```no_run
698    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
699    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
700    ///         .with_compile_args("-D MYDEF").load();
701    ///     let data = vec![0; 4];
702    ///     let les: [u64; 3] = [1, 1, 1];
703    ///     let pes: [u64; 3] = [1, 1, 1];
704    ///     let mcl_future = mcl.task("my_kernel", 1)
705    ///                 .arg(mcl_rs::TaskArg::input_slice(&data).done(true))
706    ///                 .exec(pes);
707    ///     futures::executor::block_on(mcl_future);
708    ///```  
709    pub fn done(mut self, val: bool) -> Self {
710        if val {
711            self.flags = self.flags | ArgOpt::DONE;
712        } else {
713            self.flags = self.flags & !ArgOpt::DONE;
714        }
715        return self;
716    }
717
718    /// Sets the invalid flag for the argument
719    ///
720    /// Returns the TaskArg with the preference set
721    ///
722    /// # Examples
723    ///     
724    ///```no_run
725    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
726    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
727    ///         .with_compile_args("-D MYDEF").load();
728    ///     let data = vec![0; 4];
729    ///     let les: [u64; 3] = [1, 1, 1];
730    ///     let pes: [u64; 3] = [1, 1, 1];
731    ///     let mcl_future = mcl.task("my_kernel", 1)
732    ///                 .arg(mcl_rs::TaskArg::input_slice(&data).invalid(true))
733    ///                 .exec(pes);
734    ///     futures::executor::block_on(mcl_future);
735    ///```  
736    pub fn invalid(mut self, val: bool) -> Self {
737        if val {
738            self.flags = self.flags | ArgOpt::INVALID;
739        } else {
740            self.flags = self.flags & !ArgOpt::INVALID;
741        }
742        return self;
743    }
744
745    /// Sets the read_only flag for the argument
746    ///
747    /// Returns the TaskArg with the preference set
748    ///
749    /// # Examples
750    ///     
751    ///```no_run
752    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
753    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
754    ///         .with_compile_args("-D MYDEF").load();
755    ///     let data = vec![0; 4];
756    ///     let les: [u64; 3] = [1, 1, 1];
757    ///     let pes: [u64; 3] = [1, 1, 1];
758    ///     let mcl_future = mcl.task("my_kernel", 1)
759    ///                 .arg(mcl_rs::TaskArg::input_slice(&data).read_only(true))
760    ///                 .exec(pes);
761    ///     futures::executor::block_on(mcl_future);
762    ///```  
763    pub fn read_only(mut self, val: bool) -> Self {
764        if val {
765            self.flags = self.flags | ArgOpt::RDONLY;
766        } else {
767            self.flags = self.flags & !ArgOpt::RDONLY;
768        }
769        return self;
770    }
771
772    /// Sets the write_only flag for the argument
773    ///
774    /// Returns the TaskArg with the preference set
775    ///
776    /// # Examples
777    ///     
778    ///```no_run
779    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
780    ///     mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
781    ///         .with_compile_args("-D MYDEF").load();
782    ///     let data = vec![0; 4];
783    ///     let les: [u64; 3] = [1, 1, 1];
784    ///     let pes: [u64; 3] = [1, 1, 1];
785    ///     let mcl_future = mcl.task("my_kernel", 1)
786    ///                 .arg(mcl_rs::TaskArg::input_slice(&data).write_only(true))
787    ///                 .exec(pes);
788    ///     futures::executor::block_on(mcl_future);
789    ///```
790    pub fn write_only(mut self, val: bool) -> Self {
791        if val {
792            self.flags = self.flags | ArgOpt::WRONLY;
793        } else {
794            self.flags = self.flags & !ArgOpt::WRONLY;
795        }
796        return self;
797    }
798
799    // /// Overwrites the flags already set for the argument with the given ones
800    // ///
801    // /// ## Arguments
802    // ///
803    // /// * `bit_flags` - The bit_flags to replace existing flags with
804    // ///
805    // /// Returns the TaskArg with the preference set
806    // pub fn bit_flags(mut self, bit_flags: ArgOpt) -> Self {
807
808    //     self.flags = bit_flags;
809
810    //     return self;
811    // }
812}
813
814#[cfg(feature = "versal")]
815pub struct TaskBinProps {
816    c_handle: mcl_bin_properties,
817}
818
819#[cfg(feature = "versal")]
820impl TaskBinProps {
821    pub fn new(num_devices: u64, types: u64, name: &str) -> Self {
822        TaskBinProps {
823            c_handle: mcl_bin_properties {
824                devices: num_devices,
825                types: types,
826                name: CString::new(name).unwrap().into_raw(),
827            },
828        }
829    }
830
831    pub fn get_devices(&self) -> u64 {
832        self.c_handle.devices
833    }
834
835    pub fn get_types(&self) -> u64 {
836        self.c_handle.types
837    }
838
839    pub fn get_name(&self) -> String {
840        return unsafe {
841            CString::from_raw(self.c_handle.name as *mut _)
842                .into_string()
843                .unwrap()
844        };
845    }
846}