Skip to main content

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
31// We intentionally do not implement Default for Queue as it could not always select the desired device.
32#[allow(clippy::new_without_default)]
33impl Queue {
34    /// Construct a `Queue` based on the device returned from the default selector.
35    pub fn new() -> Self {
36        Self(ffi::new_queue())
37    }
38
39    /// Construct an immediate `Queue` based on the device returned from the default selector.
40    pub fn new_immediate() -> Self {
41        Self(ffi::new_queue_immediate())
42    }
43
44    /// Returns the SYCL queue’s context.
45    pub fn get_context(&self) -> Context {
46        ffi::get_context(&self.0).into()
47    }
48
49    /// Returns the device the queue is associated with.
50    pub fn get_device(&self) -> Device {
51        ffi::get_device(&self.0).into()
52    }
53
54    /// Allocates zeroed memory and creates a host-side [`UsmBox`] that can store an array of T.
55    pub fn alloc_host<T: Pod>(&mut self, len: usize) -> Result<EnqueuedHostUsmBox<T>> {
56        unsafe {
57            let mut array = self.alloc_uninit_host(len);
58            self.memset(&mut array, 0)
59                .map(|event| EnqueuedUsmBox::new(array, event))
60        }
61    }
62
63    /// Allocates zeroed memory and creates a shared [`UsmBox`] that can store an array of T.
64    pub fn alloc_shared<T: Pod>(&mut self, len: usize) -> Result<EnqueuedSharedUsmBox<T>> {
65        unsafe {
66            let mut array = self.alloc_uninit_shared(len);
67            self.memset(&mut array, 0)
68                .map(|event| EnqueuedUsmBox::new(array, event))
69        }
70    }
71
72    /// Allocates zeroed memory and creates a device [`UsmBox`] that can store an array of T.
73    pub fn alloc_device<T: Pod>(&mut self, len: usize) -> Result<EnqueuedDeviceUsmBox<T>> {
74        unsafe {
75            let mut array = self.alloc_uninit_device(len);
76            self.memset(&mut array, 0)
77                .map(|event| EnqueuedUsmBox::new(array, event))
78        }
79    }
80
81    /// Allocates memory and creates a host-side [`UsmBox`] that can store an array of T.
82    ///
83    /// # Safety
84    ///
85    /// The array contents are uninitialized and must not be read before they are initialized.
86    pub unsafe fn alloc_uninit_host<T>(&self, len: usize) -> HostUsmBox<T> {
87        let allocator = UsmAllocator::from(self);
88        unsafe { UsmBox::new(allocator, len) }
89    }
90
91    /// Allocates memory and creates a shared [`UsmBox`] that can store an array of T.
92    ///
93    /// # Safety
94    ///
95    /// The array contents are uninitialized and must not be read before they are initialized.
96    pub unsafe fn alloc_uninit_shared<T>(&self, len: usize) -> SharedUsmBox<T> {
97        let allocator = UsmAllocator::from(self);
98        unsafe { UsmBox::new(allocator, len) }
99    }
100
101    /// Allocates memory and creates a device-side [`UsmBox`] that can store an array of T.
102    ///
103    /// # Safety
104    ///
105    /// The array contents are uninitialized and must not be read before they are initialized.
106    pub unsafe fn alloc_uninit_device<T>(&self, len: usize) -> DeviceUsmBox<T> {
107        let allocator = UsmAllocator::from(self);
108        unsafe { UsmBox::new(allocator, len) }
109    }
110
111    /// Sets memory allocated with USM allocations.
112    ///
113    /// # Safety
114    ///
115    /// The caller must ensure the underlying memory is not aliased elsewhere.
116    pub unsafe fn memset<T, A: UsmAlloc>(
117        &mut self,
118        array: &mut UsmBox<T, A>,
119        value: i32,
120    ) -> Result<Event> {
121        unsafe { self.memset_with_deps(array, value, &[]) }
122    }
123
124    /// Sets memory allocated with USM allocations after all specified events finish.
125    ///
126    /// # Safety
127    ///
128    /// The caller must ensure the underlying memory is not aliased elsewhere.
129    pub unsafe fn memset_with_deps<T, A: UsmAlloc>(
130        &mut self,
131        array: &mut UsmBox<T, A>,
132        value: i32,
133        dep_events: &[&Event],
134    ) -> Result<Event> {
135        let ptr = array.get_byte_ptr();
136        let num_bytes = array.get_byte_size();
137        let dep_events = dep_events
138            .iter()
139            .map(|e| EventPtr {
140                ptr: (*e).clone().0,
141            })
142            .collect::<Vec<_>>();
143        unsafe { ffi::memset(&mut self.0, ptr, value, num_bytes, dep_events) }.map(Into::into)
144    }
145
146    /// Submits a barrier to the queue.
147    pub fn barrier(&mut self) -> Result<Event> {
148        self.barrier_with_deps(&[])
149    }
150
151    /// Submits a barrier to the queue after all specified events finish.
152    pub fn barrier_with_deps(&mut self, dep_events: &[&Event]) -> Result<Event> {
153        let dep_events = dep_events
154            .iter()
155            .map(|e| EventPtr {
156                ptr: (*e).clone().0,
157            })
158            .collect::<Vec<_>>();
159        ffi::barrier(&mut self.0, dep_events).map(Into::into)
160    }
161
162    /// Performs a blocking wait for the completion of all enqueued tasks in the queue. Returns an
163    /// error if a synchronous SYCL exception occurs.
164    ///
165    /// Dropping the queue does not wait for its completion.
166    pub fn wait(&mut self) -> Result<()> {
167        ffi::wait(&mut self.0)
168    }
169
170    /// Enqueues a kernel object to the queue as an ND-range kernel, using the number of work-items
171    /// specified by the [`NdRange`] nd_range.
172    ///
173    /// # Safety
174    ///
175    /// The caller must make sure each argument matches the launched SYCL kernel's
176    /// signature, including their respective size, layout and alignment.
177    pub unsafe fn launch<const ARGC: usize, const DIMENSIONS: usize>(
178        &mut self,
179        nd_range: NdRange<DIMENSIONS>,
180        kernel: &Kernel,
181        args: impl KernelArgumentList<ARGC>,
182    ) -> Result<Event>
183    where
184        NdRange<DIMENSIONS>: ValidDimension,
185    {
186        unsafe { nd_range.launch(self, kernel, args) }
187    }
188
189    /// Copies the contents of the source array to the destination array.
190    ///
191    /// Panics if the source and destination array lengths differ.
192    pub fn copy<T, A1, A2>(&mut self, src: &UsmBox<T, A1>, dst: &mut UsmBox<T, A2>) -> Result<Event>
193    where
194        T: Pod,
195        A1: UsmAlloc,
196        A2: UsmAlloc,
197    {
198        self.copy_with_deps(src, dst, &[])
199    }
200
201    /// Copies the contents of the source array to the destination array after all specified
202    /// events finish.
203    ///
204    /// Panics if the source and destination array lengths differ.
205    pub fn copy_with_deps<T, A1, A2>(
206        &mut self,
207        src: &UsmBox<T, A1>,
208        dst: &mut UsmBox<T, A2>,
209        dep_events: &[&Event],
210    ) -> Result<Event>
211    where
212        T: Pod,
213        A1: UsmAlloc,
214        A2: UsmAlloc,
215    {
216        assert_eq!(
217            src.get_len(),
218            dst.get_len(),
219            "source and destination array lengths differ"
220        );
221
222        // TODO: Resolve the C++ lifetime elision issue
223        let dep_events = dep_events
224            .iter()
225            .map(|e| EventPtr {
226                ptr: (*e).clone().0,
227            })
228            .collect::<Vec<_>>();
229
230        let num_bytes = src.get_len() * size_of::<T>();
231        unsafe {
232            ffi::memcpy(
233                &mut self.0,
234                dst.get_byte_ptr(),
235                src.get_byte_ptr(),
236                num_bytes,
237                dep_events,
238            )
239        }
240        .map(Into::into)
241    }
242}
243
244impl From<&Device> for Queue {
245    fn from(value: &Device) -> Self {
246        Self(ffi::new_queue_from_device(&value.0))
247    }
248}
249
250impl From<(&Context, &Device)> for Queue {
251    fn from(value: (&Context, &Device)) -> Self {
252        Self(ffi::new_queue_from_context_and_device(
253            &value.0.0, &value.1.0,
254        ))
255    }
256}
257
258impl From<cxx::UniquePtr<ffi::Queue>> for Queue {
259    fn from(value: cxx::UniquePtr<ffi::Queue>) -> Self {
260        Self(value)
261    }
262}
263
264impl Clone for Queue {
265    fn clone(&self) -> Self {
266        ffi::clone(&self.0).into()
267    }
268}