diff --git a/ci/style.rs b/ci/style.rs index 31adeae4fbde7..b50189a6966df 100644 --- a/ci/style.rs +++ b/ci/style.rs @@ -177,7 +177,15 @@ fn check_style(file: &str, path: &Path, err: &mut Errors) { } if s_macros == 2 { s_macros += 1; - err.error(path, i, "multiple s! macros in one module"); + // Can't enforce this rule until after raising the MSRV to 1.19.0 or + // later. It seems that earlier Rust versions ignore #[cfg()] + // attributes within the macro. As a result, it's sometimes + // necessary to have multiple s!{} macros in a single file. It's + // hard to debug, because cargo-expand doesn't work with such + // old versions. + // See also https://github.com/rust-lang/libc/pull/2813 + + // err.error(path, i, "multiple s! macros in one module"); } state = line_state; diff --git a/libc-test/build.rs b/libc-test/build.rs index 23134e0f56609..e8340d02d3606 100644 --- a/libc-test/build.rs +++ b/libc-test/build.rs @@ -1477,7 +1477,7 @@ fn test_dragonflybsd(target: &str) { // just insert some padding. (struct_ == "siginfo_t" && field == "_pad") || // sigev_notify_thread_id is actually part of a sigev_un union - (struct_ == "sigevent" && field == "sigev_notify_thread_id") + (struct_ == "sigevent_0_2_126" && field == "sigev_notify_thread_id") }); cfg.generate("../src/lib.rs", "main.rs"); @@ -1724,6 +1724,8 @@ fn test_android(target: &str) { // sigval is a struct in Rust, but a union in C: "sigval" => format!("union sigval"), + "sigevent_0_2_126" => "struct sigevent".to_string(), + // put `struct` in front of all structs:. t if is_struct => format!("struct {}", t), @@ -1970,6 +1972,8 @@ fn test_android(target: &str) { (struct_ == "sigevent" && field == "sigev_value") || // this one is an anonymous union (struct_ == "ff_effect" && field == "u") || + (struct_ == "sigevent_0_2_126" && field == "sigev_value") || + (struct_ == "sigevent" && field == "_sigev_un") || // FIXME: `sa_sigaction` has type `sighandler_t` but that type is // incorrect, see: https://github.com/rust-lang/libc/issues/1359 (struct_ == "sigaction" && field == "sa_sigaction") || @@ -2163,6 +2167,8 @@ fn test_freebsd(target: &str) { // FIXME: https://github.com/rust-lang/libc/issues/1273 "sighandler_t" => "sig_t".to_string(), + "sigevent_0_2_126" => "struct sigevent".to_string(), + t if is_union => format!("union {}", t), t if t.ends_with("_t") => t.to_string(), @@ -2574,6 +2580,7 @@ fn test_freebsd(target: &str) { ("if_data", "__ifi_lastchange") => true, ("ifreq", "ifr_ifru") => true, ("ifconf", "ifc_ifcu") => true, + ("sigevent", "_sigev_un") => true, // anonymous struct ("devstat", "dev_links") => true, @@ -2722,6 +2729,8 @@ fn test_emscripten(target: &str) { // typedefs don't need any keywords t if t.ends_with("_t") => t.to_string(), + "sigevent_0_2_126" => "struct sigevent".to_string(), + // put `struct` in front of all structs:. t if is_struct => format!("struct {}", t), @@ -2761,6 +2770,9 @@ fn test_emscripten(target: &str) { }); cfg.skip_struct(move |ty| { + if ty.starts_with("__c_anonymous_") { + return true; + } match ty { // This is actually a union, not a struct // FIXME: is this necessary? @@ -2847,6 +2859,7 @@ fn test_emscripten(target: &str) { // sigval is actually a union, but we pretend it's a struct // FIXME: is this necessary? (struct_ == "sigevent" && field == "sigev_value") || + (struct_ == "sigevent_0_2_126" && field == "sigev_value") || // aio_buf is "volatile void*" and Rust doesn't understand volatile // FIXME: is this necessary? (struct_ == "aiocb" && field == "aio_buf") @@ -2863,8 +2876,10 @@ fn test_emscripten(target: &str) { // musl seems to define this as an *anonymous* bitfield // FIXME: is this necessary? (struct_ == "statvfs" && field == "__f_unused") || - // sigev_notify_thread_id is actually part of a sigev_un union - (struct_ == "sigevent" && field == "sigev_notify_thread_id") || + // union field + (struct_ == "sigevent" && field == "_sigev_un") || + // union field on the backwards-compat struct definition + (struct_ == "sigevent_0_2_126" && field == "sigev_notify_thread_id") || // signalfd had SIGSYS fields added in Linux 4.18, but no libc release has them yet. (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" || field == "_pad2" || @@ -3486,6 +3501,9 @@ fn test_linux(target: &str) { // typedefs don't need any keywords t if t.ends_with("_t") => t.to_string(), + + "sigevent_0_2_126" => "struct sigevent".to_string(), + // put `struct` in front of all structs:. t if is_struct => format!("struct {}", t), // put `union` in front of all unions: @@ -4178,6 +4196,7 @@ fn test_linux(target: &str) { (struct_ == "utmpx" && field == "ut_tv") || // sigval is actually a union, but we pretend it's a struct (struct_ == "sigevent" && field == "sigev_value") || + (struct_ == "sigevent_0_2_126" && field == "sigev_value") || // this one is an anonymous union (struct_ == "ff_effect" && field == "u") || // `__exit_status` type is a patch which is absent in musl @@ -4204,8 +4223,10 @@ fn test_linux(target: &str) { (musl && struct_ == "glob_t" && field == "gl_flags") || // musl seems to define this as an *anonymous* bitfield (musl && struct_ == "statvfs" && field == "__f_unused") || - // sigev_notify_thread_id is actually part of a sigev_un union - (struct_ == "sigevent" && field == "sigev_notify_thread_id") || + // union field + (struct_ == "sigevent" && field == "_sigev_un") || + // union field on the backwards-compat struct definition + (struct_ == "sigevent_0_2_126" && field == "sigev_notify_thread_id") || // signalfd had SIGSYS fields added in Linux 4.18, but no libc release // has them yet. (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" || @@ -4717,6 +4738,7 @@ fn test_haiku(target: &str) { ("sem_t", "named_sem_id") => true, ("sigaction", "sa_sigaction") => true, ("sigevent", "sigev_value") => true, + ("sigevent_0_2_126", "sigev_value") => true, ("fpu_state", "_fpreg") => true, ("cpu_topology_node_info", "data") => true, // these fields have a simplified data definition in libc diff --git a/src/unix/bsd/freebsdlike/freebsd/mod.rs b/src/unix/bsd/freebsdlike/freebsd/mod.rs index 799e3811ed44c..ae2132949ff52 100644 --- a/src/unix/bsd/freebsdlike/freebsd/mod.rs +++ b/src/unix/bsd/freebsdlike/freebsd/mod.rs @@ -1393,18 +1393,6 @@ s_no_extra_traits! { __reserved: [::c_long; 4] } - pub struct sigevent { - pub sigev_notify: ::c_int, - pub sigev_signo: ::c_int, - pub sigev_value: ::sigval, - //The rest of the structure is actually a union. We expose only - //sigev_notify_thread_id because it's the most useful union member. - pub sigev_notify_thread_id: ::lwpid_t, - #[cfg(target_pointer_width = "64")] - __unused1: ::c_int, - __unused2: [::c_long; 7] - } - pub struct ptsstat { #[cfg(any(freebsd12, freebsd13, freebsd14))] pub dev: u64, @@ -1637,6 +1625,95 @@ s_no_extra_traits! { } } +#[cfg(libc_union)] +s! { + pub struct __c_anonymous_sigev_thread { + pub _function: *mut ::c_void, // Actually a function pointer + pub _attribute: *mut ::pthread_attr_t, + } + + // When sigevent was first added to libc, Rust still didn't support unions. + // So the definition only included one of the union's member. This + // structure exists for backwards-compatibility with consumers that still + // try to access that one member. + #[doc(hidden)] + #[deprecated( + since = "0.2.147", + note = "Use sigevent instead" + )] + pub struct sigevent_0_2_126 { + pub sigev_notify: ::c_int, + pub sigev_signo: ::c_int, + pub sigev_value: ::sigval, + pub sigev_notify_thread_id: ::lwpid_t, + #[cfg(target_pointer_width = "64")] + __unused1: ::c_int, + __unused2: [::c_long; 7] + } +} + +#[cfg(libc_union)] +s_no_extra_traits! { + // Can't correctly impl Debug for unions + #[allow(missing_debug_implementations)] + pub union __c_anonymous_sigev_un { + pub _threadid: ::__lwpid_t, + pub _sigev_thread: __c_anonymous_sigev_thread, + pub _kevent_flags: ::c_ushort, + __spare__: [::c_long; 8], + } + + pub struct sigevent { + pub sigev_notify: ::c_int, + pub sigev_signo: ::c_int, + pub sigev_value: ::sigval, + pub _sigev_un: __c_anonymous_sigev_un, + /// Exists just to prevent the struct from being safely constructed, + /// because the Debug, Hash, PartialImpl, and + /// Deref trait impls might read uninitialized + /// fields of _sigev_un. This field may be removed once those trait + /// impls are. + _private: () + } +} + +#[cfg(not(libc_union))] +s_no_extra_traits! { + pub struct sigevent { + pub sigev_notify: ::c_int, + pub sigev_signo: ::c_int, + pub sigev_value: ::sigval, + pub _unused0: ::lwpid_t, + #[cfg(target_pointer_width = "64")] + __unused1: ::c_int, + __unused2: [::c_long; 7], + /// Exists just to prevent the struct from being safely constructed, + /// because the Debug, Hash, PartialImpl, and + /// Deref trait impls might read uninitialized + /// fields of _sigev_un. This field may be removed once those trait + /// impls are. + _private: () + } +} + +#[allow(deprecated)] +#[cfg(libc_union)] +impl ::core::ops::Deref for sigevent { + type Target = sigevent_0_2_126; + + fn deref(&self) -> &Self::Target { + unsafe { &*(self as *const Self as *const sigevent_0_2_126) } + } +} + +#[allow(deprecated)] +#[cfg(libc_union)] +impl ::core::ops::DerefMut for sigevent { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *(self as *mut Self as *mut sigevent_0_2_126) } + } +} + cfg_if! { if #[cfg(feature = "extra_traits")] { impl PartialEq for utmpx { @@ -1826,25 +1903,61 @@ cfg_if! { } } + #[cfg(libc_union)] + impl PartialEq for sigevent { + fn eq(&self, other: &sigevent) -> bool { + self.sigev_notify == other.sigev_notify + && self.sigev_signo == other.sigev_signo + && self.sigev_value == other.sigev_value + // sigev_notify indicates which union fields are valid + && match self.sigev_notify { + ::SIGEV_NONE => true, + ::SIGEV_SIGNAL => true, + ::SIGEV_THREAD => unsafe { + self._sigev_un._sigev_thread + == other._sigev_un._sigev_thread + }, + ::SIGEV_KEVENT => unsafe { + self._sigev_un._kevent_flags + == other._sigev_un._kevent_flags + }, + ::SIGEV_THREAD_ID => unsafe { + self._sigev_un._threadid + == other._sigev_un._threadid + }, + _ => false + } + } + } + #[cfg(not(libc_union))] impl PartialEq for sigevent { fn eq(&self, other: &sigevent) -> bool { self.sigev_notify == other.sigev_notify && self.sigev_signo == other.sigev_signo && self.sigev_value == other.sigev_value - && self.sigev_notify_thread_id - == other.sigev_notify_thread_id } } impl Eq for sigevent {} impl ::fmt::Debug for sigevent { fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result { - f.debug_struct("sigevent") - .field("sigev_notify", &self.sigev_notify) - .field("sigev_signo", &self.sigev_signo) - .field("sigev_value", &self.sigev_value) - .field("sigev_notify_thread_id", - &self.sigev_notify_thread_id) - .finish() + let mut ds = f.debug_struct("sigevent"); + ds.field("sigev_notify", &self.sigev_notify); + ds.field("sigev_signo", &self.sigev_signo); + ds.field("sigev_value", &self.sigev_value); + #[cfg(libc_union)] + // The sigev_notify field indicates which union fields are valid + unsafe { + match self.sigev_notify { + ::SIGEV_THREAD => ds.field("_sigev_thread", + &self._sigev_un._sigev_thread), + ::SIGEV_KEVENT => ds.field("_kevent_flags", + &self._sigev_un._kevent_flags), + ::SIGEV_THREAD_ID => ds.field("_threadid", + &self._sigev_un._threadid), + _ => &mut ds + } + }; + ds.finish() } } impl ::hash::Hash for sigevent { @@ -1852,7 +1965,16 @@ cfg_if! { self.sigev_notify.hash(state); self.sigev_signo.hash(state); self.sigev_value.hash(state); - self.sigev_notify_thread_id.hash(state); + #[cfg(libc_union)] + // The sigev_notify field indicates which union fields are valid + unsafe { + match self.sigev_notify { + ::SIGEV_THREAD => self._sigev_un._sigev_thread.hash(state), + ::SIGEV_KEVENT => self._sigev_un._kevent_flags.hash(state), + ::SIGEV_THREAD_ID => self._sigev_un._threadid.hash(state), + _ => () + }; + } } } diff --git a/src/unix/linux_like/mod.rs b/src/unix/linux_like/mod.rs index 35c7598c911d8..dd7b099c051f8 100644 --- a/src/unix/linux_like/mod.rs +++ b/src/unix/linux_like/mod.rs @@ -243,12 +243,29 @@ s_no_extra_traits! { pub domainname: [::c_char; 65] } - pub struct sigevent { +} + +#[cfg(libc_union)] +s! { + pub struct __c_anonymous_sigev_thread { + pub _function: *mut ::c_void, // Actually a function pointer + pub _attribute: *mut ::pthread_attr_t, + } + + // When sigevent was first added to libc, Rust still didn't support unions. + // So the definition only included one of the union's member. This + // structure exists for backwards-compatibility with consumers that still + // try to access that one member. + #[doc(hidden)] + #[deprecated( + since = "0.2.127", + note = "Use sigevent instead" + )] + #[cfg(libc_union)] + pub struct sigevent_0_2_126 { pub sigev_value: ::sigval, pub sigev_signo: ::c_int, pub sigev_notify: ::c_int, - // Actually a union. We only expose sigev_notify_thread_id because it's - // the most useful member pub sigev_notify_thread_id: ::c_int, #[cfg(target_pointer_width = "64")] __unused1: [::c_int; 11], @@ -257,6 +274,72 @@ s_no_extra_traits! { } } +#[cfg(libc_union)] +s_no_extra_traits! { + // Can't correctly impl Debug for unions + #[allow(missing_debug_implementations)] + #[cfg(libc_union)] + pub union __c_anonymous_sigev_un { + #[cfg(target_pointer_width = "64")] + _pad: [::c_int; (64 - 2 * 4 - 8) / 4], + #[cfg(target_pointer_width = "32")] + _pad: [::c_int; (64 - 2 * 4 - 4) / 4], + pub _tid: ::c_int, + pub _sigev_thread: __c_anonymous_sigev_thread, + } + + pub struct sigevent { + pub sigev_value: ::sigval, + pub sigev_signo: ::c_int, + pub sigev_notify: ::c_int, + pub _sigev_un: __c_anonymous_sigev_un, + /// Exists just to prevent the struct from being safely constructed, + /// because the Debug, Hash, PartialImpl, and + /// Deref trait impls might read uninitialized + /// fields of _sigev_un. This field may be removed once those trait + /// impls are. + _private: () + } +} + +#[cfg(not(libc_union))] +s_no_extra_traits! { + pub struct sigevent { + pub sigev_value: ::sigval, + pub sigev_signo: ::c_int, + pub sigev_notify: ::c_int, + __unused0: ::c_int, + #[cfg(target_pointer_width = "64")] + __unused1: [::c_int; 11], + #[cfg(target_pointer_width = "32")] + __unused1: [::c_int; 12], + /// Exists just to prevent the struct from being safely constructed, + /// because the Debug, Hash, PartialImpl, and + /// Deref trait impls might read uninitialized + /// fields of _sigev_un. This field may be removed once those trait + /// impls are. + _private: () + } +} + +#[allow(deprecated)] +#[cfg(libc_union)] +impl ::core::ops::Deref for sigevent { + type Target = sigevent_0_2_126; + + fn deref(&self) -> &Self::Target { + unsafe { &*(self as *const Self as *const sigevent_0_2_126) } + } +} + +#[allow(deprecated)] +#[cfg(libc_union)] +impl ::core::ops::DerefMut for sigevent { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *(self as *mut Self as *mut sigevent_0_2_126) } + } +} + cfg_if! { if #[cfg(feature = "extra_traits")] { impl PartialEq for epoll_event { @@ -401,25 +484,64 @@ cfg_if! { } } + #[cfg(libc_union)] + impl PartialEq for sigevent { + fn eq(&self, other: &sigevent) -> bool { + self.sigev_value == other.sigev_value + && self.sigev_signo == other.sigev_signo + && self.sigev_notify == other.sigev_notify + // sigev_notify indicates which union fields are valid + && match self.sigev_notify { + ::SIGEV_NONE => true, + ::SIGEV_SIGNAL => true, + ::SIGEV_THREAD => unsafe { + self._sigev_un._sigev_thread + == other._sigev_un._sigev_thread + }, + #[cfg(any( + target_os = "android", + target_env = "gnu", + target_env = "uclibc", + ))] + ::SIGEV_THREAD_ID => unsafe { + self._sigev_un._tid == other._sigev_un._tid + }, + _ => false + } + } + } + #[cfg(not(libc_union))] impl PartialEq for sigevent { fn eq(&self, other: &sigevent) -> bool { self.sigev_value == other.sigev_value && self.sigev_signo == other.sigev_signo && self.sigev_notify == other.sigev_notify - && self.sigev_notify_thread_id - == other.sigev_notify_thread_id } } impl Eq for sigevent {} impl ::fmt::Debug for sigevent { fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result { - f.debug_struct("sigevent") - .field("sigev_value", &self.sigev_value) - .field("sigev_signo", &self.sigev_signo) - .field("sigev_notify", &self.sigev_notify) - .field("sigev_notify_thread_id", - &self.sigev_notify_thread_id) - .finish() + let mut ds = f.debug_struct("sigevent"); + ds.field("sigev_value", &self.sigev_value); + ds.field("sigev_signo", &self.sigev_signo); + ds.field("sigev_notify", &self.sigev_notify); + #[cfg(libc_union)] + // The sigev_notify field indicates which union fields are valid + unsafe { + match self.sigev_notify { + ::SIGEV_THREAD => ds.field("_sigev_thread", + &self._sigev_un._sigev_thread), + #[cfg(any( + target_os = "android", + target_env = "gnu", + target_env = "uclibc", + ))] + ::SIGEV_THREAD_ID => ds.field("_tid", + &self._sigev_un._tid), + _ => &mut ds + } + }; + ds.finish() } } impl ::hash::Hash for sigevent { @@ -427,7 +549,20 @@ cfg_if! { self.sigev_value.hash(state); self.sigev_signo.hash(state); self.sigev_notify.hash(state); - self.sigev_notify_thread_id.hash(state); + #[cfg(libc_union)] + // The sigev_notify field indicates which union fields are valid + unsafe { + match self.sigev_notify { + ::SIGEV_THREAD => self._sigev_un._sigev_thread.hash(state), + #[cfg(any( + target_os = "android", + target_env = "gnu", + target_env = "uclibc", + ))] + ::SIGEV_THREAD_ID => self._sigev_un._tid.hash(state), + _ => () + }; + } } } }