Skip to content

Commit

Permalink
Fix the definition of sigevent
Browse files Browse the repository at this point in the history
It was originally defined back before rust could represent C unions.  So
instead of defining the union field correctly, it simply defined that
union's most useful field.  Define it correctly now.

Include a backwards-compatibility mechanism: Rename sigevent's old
definition and hide it.  Implement Deref and DerefMut from sigevent to
the old definition, so consumers will still be able to use the old field
name.
  • Loading branch information
asomers committed Jun 14, 2022
1 parent eecb648 commit a9ad9b6
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 40 deletions.
31 changes: 24 additions & 7 deletions libc-test/build.rs
Expand Up @@ -1409,7 +1409,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");
Expand Down Expand Up @@ -1645,6 +1645,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),

Expand Down Expand Up @@ -1802,8 +1804,10 @@ fn test_android(target: &str) {
// 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") ||
// 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 Android 4.19, but CI does not have that version yet.
(struct_ == "signalfd_siginfo" && (field == "ssi_syscall" ||
field == "ssi_call_addr" ||
Expand Down Expand Up @@ -1980,6 +1984,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(),
Expand Down Expand Up @@ -2270,6 +2276,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,
Expand Down Expand Up @@ -2409,6 +2416,8 @@ fn test_emscripten(target: &str) {

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),

Expand Down Expand Up @@ -2512,8 +2521,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" ||
Expand Down Expand Up @@ -2863,6 +2874,8 @@ fn test_linux(target: &str) {
// In MUSL `flock64` is a typedef to `flock`.
"flock64" if musl => format!("struct {}", ty),

"sigevent_0_2_126" => "struct sigevent".to_string(),

// put `struct` in front of all structs:.
t if is_struct => format!("struct {}", t),

Expand Down Expand Up @@ -3286,6 +3299,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
Expand All @@ -3312,8 +3326,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" ||
Expand Down Expand Up @@ -3810,6 +3826,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,
// these fields have a simplified data definition in libc
("fpu_state", "_xmm") => true,
Expand Down
111 changes: 95 additions & 16 deletions src/unix/bsd/freebsdlike/freebsd/mod.rs
Expand Up @@ -871,6 +871,12 @@ s! {
pub profhz: ::c_int,
}

#[cfg(libc_union)]
pub struct __c_anonymous_sigev_thread {
pub _function: *mut ::c_void, // Actually a function pointer
pub _attribute: *mut ::pthread_attr_t,
}

pub struct __c_anonymous_stailq_entry_devstat {
pub stqe_next: *mut devstat,
}
Expand Down Expand Up @@ -990,6 +996,21 @@ s! {
pub _flags: u32,
pub _clockid: u32,
}

// 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)]
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]
}
}

s_no_extra_traits! {
Expand Down Expand Up @@ -1040,16 +1061,22 @@ s_no_extra_traits! {
__reserved: [::c_long; 4]
}

// Can't correctly impl Debug for unions
#[allow(missing_debug_implementations)]
#[cfg(libc_union)]
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],
}

#[cfg(libc_union)]
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 _sigev_un: __c_anonymous_sigev_un,
}

pub struct ptsstat {
Expand Down Expand Up @@ -1202,6 +1229,24 @@ s_no_extra_traits! {
}
}

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)
}
}
}

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 {
Expand Down Expand Up @@ -1396,28 +1441,62 @@ cfg_if! {
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
// 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
}
}
}
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);
// 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 {
fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
self.sigev_notify.hash(state);
self.sigev_signo.hash(state);
self.sigev_value.hash(state);
self.sigev_notify_thread_id.hash(state);
// 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),
_ => ()
};
}
}
}

Expand Down

0 comments on commit a9ad9b6

Please sign in to comment.