sycl_rs/
queue.rs

1//
2// Copyright (C) 2026 Intel Corporation
3//
4// Under the MIT License or the Apache License v2.0.
5// See LICENSE-MIT and LICENSE-APACHE for license information.
6// SPDX-License-Identifier: MIT OR Apache-2.0
7//
8
9use crate::Result;
10use bytemuck::Pod;
11use sycl_rs_sys::{queue::ffi, types::ffi::EventPtr};
12
13use crate::{
14    context::Context,
15    device::Device,
16    event::Event,
17    kernel::{Kernel, KernelArgumentList},
18    range::{NdRange, ValidDimension},
19    usm::{UsmAlloc, UsmAllocator},
20    usmbox::{
21        DeviceUsmBox, EnqueuedDeviceUsmBox, EnqueuedHostUsmBox, EnqueuedSharedUsmBox,
22        EnqueuedUsmBox, HostUsmBox, SharedUsmBox, UsmBox,
23    },
24};
25
26/// The `Queue` connects a host program to a single device. Programs submit tasks to a device via the
27/// `Queue` and may monitor the `Queue` for completion. A program initiates the task by submitting
28/// a kernel.
29pub struct Queue(pub(crate) cxx::UniquePtr<ffi::Queue>);
30
31impl Queue {
32    /// Construct a `Queue` based on the device returned from the default selector.
33    pub fn new() -> Self {
34        Self(ffi::new_queue())
35    }
36
37    /// Construct an immediate `Queue` based on the device returned from the default selector.
38    pub fn new_immediate() -> Self {
39        Self(ffi::new_queue_immediate())
40    }
41
42    /// Returns the SYCL queue’s context.
43    pub fn get_context(&self) -> Context {
44        ffi::get_context(&self.0).into()
45    }
46
47    /// Returns the device the queue is associated with.
48    pub fn get_device(&self) -> Device {
49        ffi::get_device(&self.0).into()
50    }
51
52    /// Allocates zeroed memory and creates a host-side [`UsmBox`] that can store an array of T.
53    pub fn alloc_host<T: Pod>(&mut self, len: usize) -> Result<EnqueuedHostUsmBox<T>> {
54        unsafe {
55            let mut array = self.alloc_uninit_host(len);
56            self.memset(&mut array, 0)
57                .map(|event| EnqueuedUsmBox::new(array, event))
58        }
59    }
60
61    /// Allocates zeroed memory and creates a shared [`UsmBox`] that can store an array of T.
62    pub fn alloc_shared<T: Pod>(&mut self, len: usize) -> Result<EnqueuedSharedUsmBox<T>> {
63        unsafe {
64            let mut array = self.alloc_uninit_shared(len);
65            self.memset(&mut array, 0)
66                .map(|event| EnqueuedUsmBox::new(array, event))
67        }
68    }
69
70    /// Allocates zeroed memory and creates a device [`UsmBox`] that can store an array of T.
71    pub fn alloc_device<T: Pod>(&mut self, len: usize) -> Result<EnqueuedDeviceUsmBox<T>> {
72        unsafe {
73            let mut array = self.alloc_uninit_device(len);
74            self.memset(&mut array, 0)
75                .map(|event| EnqueuedUsmBox::new(array, event))
76        }
77    }
78
79    /// Allocates memory and creates a host-side [`UsmBox`] that can store an array of T.
80    /// Safety: the array contents are uninitialized.
81    pub unsafe fn alloc_uninit_host<T>(&self, len: usize) -> HostUsmBox<T> {
82        let allocator = UsmAllocator::from(self);
83        unsafe { UsmBox::new(allocator, len) }
84    }
85
86    /// Allocates memory and creates a shared [`UsmBox`] that can store an array of T.
87    /// Safety: the array contents are uninitialized.
88    pub unsafe fn alloc_uninit_shared<T>(&self, len: usize) -> SharedUsmBox<T> {
89        let allocator = UsmAllocator::from(self);
90        unsafe { UsmBox::new(allocator, len) }
91    }
92
93    /// Allocates memory and creates a device-side [`UsmBox`] that can store an array of T.
94    /// Safety: the array contents are uninitialized.
95    pub unsafe fn alloc_uninit_device<T>(&self, len: usize) -> DeviceUsmBox<T> {
96        let allocator = UsmAllocator::from(self);
97        unsafe { UsmBox::new(allocator, len) }
98    }
99
100    /// Sets memory allocated with USM allocations.
101    /// Safety: the caller must make sure the underlying memory isn't being aliased somewhere else.
102    pub unsafe fn memset<T, A: UsmAlloc>(
103        &mut self,
104        array: &mut UsmBox<T, A>,
105        value: i32,
106    ) -> Result<Event> {
107        unsafe { self.memset_with_deps(array, value, &[]) }
108    }
109
110    /// Sets memory allocated with USM allocations after all specified events finish.
111    /// Safety: the caller must make sure the underlying memory isn't being aliased somewhere else.
112    pub unsafe fn memset_with_deps<T, A: UsmAlloc>(
113        &mut self,
114        array: &mut UsmBox<T, A>,
115        value: i32,
116        dep_events: &[&Event],
117    ) -> Result<Event> {
118        let ptr = array.get_byte_ptr();
119        let num_bytes = array.get_byte_size();
120        let dep_events = dep_events
121            .iter()
122            .map(|e| EventPtr {
123                ptr: (*e).clone().0,
124            })
125            .collect::<Vec<_>>();
126        unsafe { ffi::memset(&mut self.0, ptr, value, num_bytes, dep_events) }.map(Into::into)
127    }
128
129    /// Submits a barrier to the queue.
130    pub fn barrier(&mut self) -> Result<Event> {
131        self.barrier_with_deps(&[]).map(Into::into)
132    }
133
134    /// Submits a barrier to the queue after all specified events finish.
135    pub fn barrier_with_deps(&mut self, dep_events: &[&Event]) -> Result<Event> {
136        let dep_events = dep_events
137            .iter()
138            .map(|e| EventPtr {
139                ptr: (*e).clone().0,
140            })
141            .collect::<Vec<_>>();
142        ffi::barrier(&mut self.0, dep_events).map(Into::into)
143    }
144
145    /// Performs a blocking wait for the completion of all enqueued tasks in the queue. Returns an
146    /// error if a synchronous SYCL exception occurs.
147    ///
148    /// Dropping the queue does not wait for its completion.
149    pub fn wait(&mut self) -> Result<()> {
150        ffi::wait(&mut self.0)
151    }
152
153    /// Enqueues a kernel object to the queue as an ND-range kernel, using the number of work-items
154    /// specified by the [`NdRange`] nd_range.
155    ///
156    /// Safety: The caller must make sure each argument matches the launched SYCL kernel's
157    /// signature, including their respective size, layout and alignment.
158    pub unsafe fn launch<const ARGC: usize, const DIMENSIONS: usize>(
159        &mut self,
160        nd_range: NdRange<DIMENSIONS>,
161        kernel: &Kernel,
162        args: impl KernelArgumentList<ARGC>,
163    ) -> Result<Event>
164    where
165        NdRange<DIMENSIONS>: ValidDimension,
166    {
167        unsafe { nd_range.launch(self, kernel, args) }
168    }
169
170    /// Copies the contents of the source array to the destination array.
171    ///
172    /// Panics if the source and destination array lengths differ.
173    pub fn copy<T, A1, A2>(&mut self, src: &UsmBox<T, A1>, dst: &mut UsmBox<T, A2>) -> Result<Event>
174    where
175        T: Pod,
176        A1: UsmAlloc,
177        A2: UsmAlloc,
178    {
179        self.copy_with_deps(src, dst, &[])
180    }
181
182    /// Copies the contents of the source array to the destination array after all specified
183    /// events finish.
184    ///
185    /// Panics if the source and destination array lengths differ.
186    pub fn copy_with_deps<T, A1, A2>(
187        &mut self,
188        src: &UsmBox<T, A1>,
189        dst: &mut UsmBox<T, A2>,
190        dep_events: &[&Event],
191    ) -> Result<Event>
192    where
193        T: Pod,
194        A1: UsmAlloc,
195        A2: UsmAlloc,
196    {
197        assert_eq!(
198            src.get_len(),
199            dst.get_len(),
200            "source and destination array lengths differ"
201        );
202
203        // TODO: Resolve the C++ lifetime elision issue
204        let dep_events = dep_events
205            .iter()
206            .map(|e| EventPtr {
207                ptr: (*e).clone().0,
208            })
209            .collect::<Vec<_>>();
210
211        let num_bytes = src.get_len() * size_of::<T>();
212        unsafe {
213            ffi::memcpy(
214                &mut self.0,
215                dst.get_byte_ptr(),
216                src.get_byte_ptr(),
217                num_bytes,
218                dep_events,
219            )
220        }
221        .map(Into::into)
222    }
223}
224
225impl From<&Device> for Queue {
226    fn from(value: &Device) -> Self {
227        Self(ffi::new_queue_from_device(&value.0))
228    }
229}
230
231impl From<(&Context, &Device)> for Queue {
232    fn from(value: (&Context, &Device)) -> Self {
233        Self(ffi::new_queue_from_context_and_device(
234            &value.0.0, &value.1.0,
235        ))
236    }
237}
238
239impl From<cxx::UniquePtr<ffi::Queue>> for Queue {
240    fn from(value: cxx::UniquePtr<ffi::Queue>) -> Self {
241        Self(value)
242    }
243}
244
245impl Clone for Queue {
246    fn clone(&self) -> Self {
247        ffi::clone(&self.0).into()
248    }
249}