rustix/backend/libc/event/
syscalls.rs

1//! libc syscalls supporting `rustix::event`.
2
3use crate::backend::c;
4#[cfg(any(linux_kernel, solarish, target_os = "redox"))]
5use crate::backend::conv::ret;
6use crate::backend::conv::ret_c_int;
7#[cfg(feature = "alloc")]
8#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
9use crate::backend::conv::ret_u32;
10#[cfg(solarish)]
11use crate::event::port::Event;
12#[cfg(any(
13    linux_kernel,
14    target_os = "freebsd",
15    target_os = "illumos",
16    target_os = "espidf"
17))]
18use crate::event::EventfdFlags;
19#[cfg(any(bsd, linux_kernel, target_os = "wasi"))]
20use crate::event::FdSetElement;
21use crate::event::PollFd;
22use crate::io;
23#[cfg(solarish)]
24use crate::utils::as_mut_ptr;
25#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
26use crate::utils::as_ptr;
27#[cfg(any(
28    all(feature = "alloc", bsd),
29    solarish,
30    all(feature = "alloc", any(linux_kernel, target_os = "redox")),
31))]
32use core::mem::MaybeUninit;
33#[cfg(any(bsd, linux_kernel, target_os = "wasi"))]
34use core::ptr::null;
35#[cfg(any(bsd, linux_kernel, solarish, target_os = "redox", target_os = "wasi"))]
36use core::ptr::null_mut;
37#[cfg(any(
38    linux_kernel,
39    solarish,
40    target_os = "redox",
41    all(feature = "alloc", bsd)
42))]
43use {crate::backend::conv::borrowed_fd, crate::fd::BorrowedFd};
44#[cfg(any(
45    linux_kernel,
46    solarish,
47    target_os = "freebsd",
48    target_os = "illumos",
49    target_os = "espidf",
50    target_os = "redox",
51    all(feature = "alloc", bsd)
52))]
53use {crate::backend::conv::ret_owned_fd, crate::fd::OwnedFd};
54#[cfg(all(feature = "alloc", bsd))]
55use {crate::event::kqueue::Event, crate::utils::as_ptr};
56
57#[cfg(any(
58    linux_kernel,
59    target_os = "freebsd",
60    target_os = "illumos",
61    target_os = "espidf"
62))]
63pub(crate) fn eventfd(initval: u32, flags: EventfdFlags) -> io::Result<OwnedFd> {
64    #[cfg(linux_kernel)]
65    unsafe {
66        syscall! {
67            fn eventfd2(
68                initval: c::c_uint,
69                flags: c::c_int
70            ) via SYS_eventfd2 -> c::c_int
71        }
72        ret_owned_fd(eventfd2(initval, bitflags_bits!(flags)))
73    }
74
75    // `eventfd` was added in FreeBSD 13, so it isn't available on FreeBSD 12.
76    #[cfg(target_os = "freebsd")]
77    unsafe {
78        weakcall! {
79            fn eventfd(
80                initval: c::c_uint,
81                flags: c::c_int
82            ) -> c::c_int
83        }
84        ret_owned_fd(eventfd(initval, bitflags_bits!(flags)))
85    }
86
87    #[cfg(any(target_os = "illumos", target_os = "espidf"))]
88    unsafe {
89        ret_owned_fd(c::eventfd(initval, bitflags_bits!(flags)))
90    }
91}
92
93#[cfg(all(feature = "alloc", bsd))]
94pub(crate) fn kqueue() -> io::Result<OwnedFd> {
95    unsafe { ret_owned_fd(c::kqueue()) }
96}
97
98#[cfg(all(feature = "alloc", bsd))]
99pub(crate) unsafe fn kevent(
100    kq: BorrowedFd<'_>,
101    changelist: &[Event],
102    eventlist: &mut [MaybeUninit<Event>],
103    timeout: Option<&c::timespec>,
104) -> io::Result<c::c_int> {
105    ret_c_int(c::kevent(
106        borrowed_fd(kq),
107        changelist.as_ptr().cast(),
108        changelist
109            .len()
110            .try_into()
111            .map_err(|_| io::Errno::OVERFLOW)?,
112        eventlist.as_mut_ptr().cast(),
113        eventlist
114            .len()
115            .try_into()
116            .map_err(|_| io::Errno::OVERFLOW)?,
117        timeout.map_or(null(), as_ptr),
118    ))
119}
120
121#[inline]
122pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: c::c_int) -> io::Result<usize> {
123    let nfds = fds
124        .len()
125        .try_into()
126        .map_err(|_convert_err| io::Errno::INVAL)?;
127
128    ret_c_int(unsafe { c::poll(fds.as_mut_ptr().cast(), nfds, timeout) })
129        .map(|nready| nready as usize)
130}
131
132#[cfg(any(bsd, linux_kernel))]
133pub(crate) unsafe fn select(
134    nfds: i32,
135    readfds: Option<&mut [FdSetElement]>,
136    writefds: Option<&mut [FdSetElement]>,
137    exceptfds: Option<&mut [FdSetElement]>,
138    timeout: Option<&crate::timespec::Timespec>,
139) -> io::Result<i32> {
140    let len = crate::event::fd_set_num_elements_for_bitvector(nfds);
141
142    let readfds = match readfds {
143        Some(readfds) => {
144            assert!(readfds.len() >= len);
145            readfds.as_mut_ptr()
146        }
147        None => null_mut(),
148    };
149    let writefds = match writefds {
150        Some(writefds) => {
151            assert!(writefds.len() >= len);
152            writefds.as_mut_ptr()
153        }
154        None => null_mut(),
155    };
156    let exceptfds = match exceptfds {
157        Some(exceptfds) => {
158            assert!(exceptfds.len() >= len);
159            exceptfds.as_mut_ptr()
160        }
161        None => null_mut(),
162    };
163
164    let timeout_data;
165    let timeout_ptr = match timeout {
166        Some(timeout) => {
167            // Convert from `Timespec` to `c::timeval`.
168            timeout_data = c::timeval {
169                tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
170                tv_usec: ((timeout.tv_nsec + 999) / 1000) as _,
171            };
172            &timeout_data
173        }
174        None => null(),
175    };
176
177    // On Apple platforms, use the specially mangled `select` which doesn't
178    // have an `FD_SETSIZE` limitation.
179    #[cfg(apple)]
180    {
181        extern "C" {
182            #[link_name = "select$DARWIN_EXTSN$NOCANCEL"]
183            fn select(
184                nfds: c::c_int,
185                readfds: *mut FdSetElement,
186                writefds: *mut FdSetElement,
187                errorfds: *mut FdSetElement,
188                timeout: *const c::timeval,
189            ) -> c::c_int;
190        }
191
192        ret_c_int(select(nfds, readfds, writefds, exceptfds, timeout_ptr))
193    }
194
195    // Otherwise just use the normal `select`.
196    #[cfg(not(apple))]
197    {
198        ret_c_int(c::select(
199            nfds,
200            readfds.cast(),
201            writefds.cast(),
202            exceptfds.cast(),
203            timeout_ptr as *mut c::timeval,
204        ))
205    }
206}
207
208// WASI uses a count + array instead of a bitvector.
209#[cfg(target_os = "wasi")]
210pub(crate) unsafe fn select(
211    nfds: i32,
212    readfds: Option<&mut [FdSetElement]>,
213    writefds: Option<&mut [FdSetElement]>,
214    exceptfds: Option<&mut [FdSetElement]>,
215    timeout: Option<&crate::timespec::Timespec>,
216) -> io::Result<i32> {
217    let len = crate::event::fd_set_num_elements_for_fd_array(nfds as usize);
218
219    let readfds = match readfds {
220        Some(readfds) => {
221            assert!(readfds.len() >= len);
222            readfds.as_mut_ptr()
223        }
224        None => null_mut(),
225    };
226    let writefds = match writefds {
227        Some(writefds) => {
228            assert!(writefds.len() >= len);
229            writefds.as_mut_ptr()
230        }
231        None => null_mut(),
232    };
233    let exceptfds = match exceptfds {
234        Some(exceptfds) => {
235            assert!(exceptfds.len() >= len);
236            exceptfds.as_mut_ptr()
237        }
238        None => null_mut(),
239    };
240
241    let timeout_data;
242    let timeout_ptr = match timeout {
243        Some(timeout) => {
244            // Convert from `Timespec` to `c::timeval`.
245            timeout_data = c::timeval {
246                tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
247                tv_usec: ((timeout.tv_nsec + 999) / 1000) as _,
248            };
249            &timeout_data
250        }
251        None => null(),
252    };
253
254    ret_c_int(c::select(
255        nfds,
256        readfds.cast(),
257        writefds.cast(),
258        exceptfds.cast(),
259        timeout_ptr as *mut c::timeval,
260    ))
261}
262
263#[cfg(solarish)]
264pub(crate) fn port_create() -> io::Result<OwnedFd> {
265    unsafe { ret_owned_fd(c::port_create()) }
266}
267
268#[cfg(solarish)]
269pub(crate) unsafe fn port_associate(
270    port: BorrowedFd<'_>,
271    source: c::c_int,
272    object: c::uintptr_t,
273    events: c::c_int,
274    user: *mut c::c_void,
275) -> io::Result<()> {
276    ret(c::port_associate(
277        borrowed_fd(port),
278        source,
279        object,
280        events,
281        user,
282    ))
283}
284
285#[cfg(solarish)]
286pub(crate) unsafe fn port_dissociate(
287    port: BorrowedFd<'_>,
288    source: c::c_int,
289    object: c::uintptr_t,
290) -> io::Result<()> {
291    ret(c::port_dissociate(borrowed_fd(port), source, object))
292}
293
294#[cfg(solarish)]
295pub(crate) fn port_get(
296    port: BorrowedFd<'_>,
297    timeout: Option<&mut c::timespec>,
298) -> io::Result<Event> {
299    let mut event = MaybeUninit::<c::port_event>::uninit();
300    let timeout = timeout.map_or(null_mut(), as_mut_ptr);
301
302    unsafe {
303        ret(c::port_get(borrowed_fd(port), event.as_mut_ptr(), timeout))?;
304    }
305
306    // If we're done, initialize the event and return it.
307    Ok(Event(unsafe { event.assume_init() }))
308}
309
310#[cfg(all(feature = "alloc", solarish))]
311pub(crate) fn port_getn(
312    port: BorrowedFd<'_>,
313    timeout: Option<&mut c::timespec>,
314    events: &mut Vec<Event>,
315    mut nget: u32,
316) -> io::Result<()> {
317    // `port_getn` special-cases a max value of 0 to be a query that returns
318    // the number of events. We don't want to do the `set_len` in that case, so
319    // so bail out early if needed.
320    if events.capacity() == 0 {
321        return Ok(());
322    }
323
324    let timeout = timeout.map_or(null_mut(), as_mut_ptr);
325    unsafe {
326        ret(c::port_getn(
327            borrowed_fd(port),
328            events.as_mut_ptr().cast(),
329            events.capacity().try_into().unwrap(),
330            &mut nget,
331            timeout,
332        ))?;
333    }
334
335    // Update the vector length.
336    unsafe {
337        events.set_len(nget.try_into().unwrap());
338    }
339
340    Ok(())
341}
342
343#[cfg(solarish)]
344pub(crate) fn port_getn_query(port: BorrowedFd<'_>) -> io::Result<u32> {
345    let mut nget: u32 = 0;
346
347    // Pass a `max` of 0 to query the number of available events.
348    unsafe {
349        ret(c::port_getn(
350            borrowed_fd(port),
351            null_mut(),
352            0,
353            &mut nget,
354            null_mut(),
355        ))?;
356    }
357
358    Ok(nget)
359}
360
361#[cfg(solarish)]
362pub(crate) fn port_send(
363    port: BorrowedFd<'_>,
364    events: c::c_int,
365    userdata: *mut c::c_void,
366) -> io::Result<()> {
367    unsafe { ret(c::port_send(borrowed_fd(port), events, userdata)) }
368}
369
370#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
371pub(crate) fn pause() {
372    let r = unsafe { c::pause() };
373    let errno = libc_errno::errno().0;
374    debug_assert_eq!(r, -1);
375    debug_assert_eq!(errno, c::EINTR);
376}
377
378#[inline]
379#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
380pub(crate) fn epoll_create(flags: super::epoll::CreateFlags) -> io::Result<OwnedFd> {
381    unsafe { ret_owned_fd(c::epoll_create1(bitflags_bits!(flags))) }
382}
383
384#[inline]
385#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
386pub(crate) fn epoll_add(
387    epoll: BorrowedFd<'_>,
388    source: BorrowedFd<'_>,
389    event: &crate::event::epoll::Event,
390) -> io::Result<()> {
391    // We use our own `Event` struct instead of libc's because
392    // ours preserves pointer provenance instead of just using a `u64`,
393    // and we have tests elsewhere for layout equivalence.
394    unsafe {
395        ret(c::epoll_ctl(
396            borrowed_fd(epoll),
397            c::EPOLL_CTL_ADD,
398            borrowed_fd(source),
399            // The event is read-only even though libc has a non-const pointer.
400            as_ptr(event) as *mut c::epoll_event,
401        ))
402    }
403}
404
405#[inline]
406#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
407pub(crate) fn epoll_mod(
408    epoll: BorrowedFd<'_>,
409    source: BorrowedFd<'_>,
410    event: &crate::event::epoll::Event,
411) -> io::Result<()> {
412    unsafe {
413        ret(c::epoll_ctl(
414            borrowed_fd(epoll),
415            c::EPOLL_CTL_MOD,
416            borrowed_fd(source),
417            // The event is read-only even though libc has a non-const pointer.
418            as_ptr(event) as *mut c::epoll_event,
419        ))
420    }
421}
422
423#[inline]
424#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
425pub(crate) fn epoll_del(epoll: BorrowedFd<'_>, source: BorrowedFd<'_>) -> io::Result<()> {
426    unsafe {
427        ret(c::epoll_ctl(
428            borrowed_fd(epoll),
429            c::EPOLL_CTL_DEL,
430            borrowed_fd(source),
431            null_mut(),
432        ))
433    }
434}
435
436#[inline]
437#[cfg(feature = "alloc")]
438#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
439pub(crate) fn epoll_wait(
440    epoll: BorrowedFd<'_>,
441    events: &mut [MaybeUninit<crate::event::epoll::Event>],
442    timeout: c::c_int,
443) -> io::Result<usize> {
444    unsafe {
445        ret_u32(c::epoll_wait(
446            borrowed_fd(epoll),
447            events.as_mut_ptr().cast::<c::epoll_event>(),
448            events.len().try_into().unwrap_or(i32::MAX),
449            timeout,
450        ))
451        .map(|i| i.try_into().unwrap_or(usize::MAX))
452    }
453}