Skip to content

Commit ec3ef17

Browse files
committed
Fix the definition of sigevent
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.
1 parent eecb648 commit ec3ef17

File tree

3 files changed

+216
-40
lines changed

3 files changed

+216
-40
lines changed

libc-test/build.rs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,7 +1409,7 @@ fn test_dragonflybsd(target: &str) {
14091409
// just insert some padding.
14101410
(struct_ == "siginfo_t" && field == "_pad") ||
14111411
// sigev_notify_thread_id is actually part of a sigev_un union
1412-
(struct_ == "sigevent" && field == "sigev_notify_thread_id")
1412+
(struct_ == "sigevent_0_2_126" && field == "sigev_notify_thread_id")
14131413
});
14141414

14151415
cfg.generate("../src/lib.rs", "main.rs");
@@ -1645,6 +1645,8 @@ fn test_android(target: &str) {
16451645
// sigval is a struct in Rust, but a union in C:
16461646
"sigval" => format!("union sigval"),
16471647

1648+
"sigevent_0_2_126" => "struct sigevent".to_string(),
1649+
16481650
// put `struct` in front of all structs:.
16491651
t if is_struct => format!("struct {}", t),
16501652

@@ -1802,8 +1804,10 @@ fn test_android(target: &str) {
18021804
// FIXME: `sa_sigaction` has type `sighandler_t` but that type is
18031805
// incorrect, see: https://github.com/rust-lang/libc/issues/1359
18041806
(struct_ == "sigaction" && field == "sa_sigaction") ||
1805-
// sigev_notify_thread_id is actually part of a sigev_un union
1806-
(struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
1807+
// union field
1808+
(struct_ == "sigevent" && field == "_sigev_un") ||
1809+
// union field on the backwards-compat struct definition
1810+
(struct_ == "sigevent_0_2_126" && field == "sigev_notify_thread_id") ||
18071811
// signalfd had SIGSYS fields added in Android 4.19, but CI does not have that version yet.
18081812
(struct_ == "signalfd_siginfo" && (field == "ssi_syscall" ||
18091813
field == "ssi_call_addr" ||
@@ -1980,6 +1984,8 @@ fn test_freebsd(target: &str) {
19801984
// FIXME: https://github.com/rust-lang/libc/issues/1273
19811985
"sighandler_t" => "sig_t".to_string(),
19821986

1987+
"sigevent_0_2_126" => "struct sigevent".to_string(),
1988+
19831989
t if is_union => format!("union {}", t),
19841990

19851991
t if t.ends_with("_t") => t.to_string(),
@@ -2270,6 +2276,7 @@ fn test_freebsd(target: &str) {
22702276
("if_data", "__ifi_lastchange") => true,
22712277
("ifreq", "ifr_ifru") => true,
22722278
("ifconf", "ifc_ifcu") => true,
2279+
("sigevent", "_sigev_un") => true,
22732280

22742281
// anonymous struct
22752282
("devstat", "dev_links") => true,
@@ -2409,6 +2416,8 @@ fn test_emscripten(target: &str) {
24092416

24102417
t if t.ends_with("_t") => t.to_string(),
24112418

2419+
"sigevent_0_2_126" => "struct sigevent".to_string(),
2420+
24122421
// put `struct` in front of all structs:.
24132422
t if is_struct => format!("struct {}", t),
24142423

@@ -2512,8 +2521,10 @@ fn test_emscripten(target: &str) {
25122521
// musl seems to define this as an *anonymous* bitfield
25132522
// FIXME: is this necessary?
25142523
(struct_ == "statvfs" && field == "__f_unused") ||
2515-
// sigev_notify_thread_id is actually part of a sigev_un union
2516-
(struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
2524+
// union field
2525+
(struct_ == "sigevent" && field == "_sigev_un") ||
2526+
// union field on the backwards-compat struct definition
2527+
(struct_ == "sigevent_0_2_126" && field == "sigev_notify_thread_id") ||
25172528
// signalfd had SIGSYS fields added in Linux 4.18, but no libc release has them yet.
25182529
(struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
25192530
field == "_pad2" ||
@@ -2863,6 +2874,8 @@ fn test_linux(target: &str) {
28632874
// In MUSL `flock64` is a typedef to `flock`.
28642875
"flock64" if musl => format!("struct {}", ty),
28652876

2877+
"sigevent_0_2_126" => "struct sigevent".to_string(),
2878+
28662879
// put `struct` in front of all structs:.
28672880
t if is_struct => format!("struct {}", t),
28682881

@@ -3286,6 +3299,7 @@ fn test_linux(target: &str) {
32863299
(struct_ == "utmpx" && field == "ut_tv") ||
32873300
// sigval is actually a union, but we pretend it's a struct
32883301
(struct_ == "sigevent" && field == "sigev_value") ||
3302+
(struct_ == "sigevent_0_2_126" && field == "sigev_value") ||
32893303
// this one is an anonymous union
32903304
(struct_ == "ff_effect" && field == "u") ||
32913305
// `__exit_status` type is a patch which is absent in musl
@@ -3312,8 +3326,10 @@ fn test_linux(target: &str) {
33123326
(musl && struct_ == "glob_t" && field == "gl_flags") ||
33133327
// musl seems to define this as an *anonymous* bitfield
33143328
(musl && struct_ == "statvfs" && field == "__f_unused") ||
3315-
// sigev_notify_thread_id is actually part of a sigev_un union
3316-
(struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
3329+
// union field
3330+
(struct_ == "sigevent" && field == "_sigev_un") ||
3331+
// union field on the backwards-compat struct definition
3332+
(struct_ == "sigevent_0_2_126" && field == "sigev_notify_thread_id") ||
33173333
// signalfd had SIGSYS fields added in Linux 4.18, but no libc release
33183334
// has them yet.
33193335
(struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
@@ -3810,6 +3826,7 @@ fn test_haiku(target: &str) {
38103826
("sem_t", "named_sem_id") => true,
38113827
("sigaction", "sa_sigaction") => true,
38123828
("sigevent", "sigev_value") => true,
3829+
("sigevent_0_2_126", "sigev_value") => true,
38133830
("fpu_state", "_fpreg") => true,
38143831
// these fields have a simplified data definition in libc
38153832
("fpu_state", "_xmm") => true,

src/unix/bsd/freebsdlike/freebsd/mod.rs

Lines changed: 91 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,12 @@ s! {
871871
pub profhz: ::c_int,
872872
}
873873

874+
#[cfg(libc_union)]
875+
pub struct __c_anonymous_sigev_thread {
876+
pub _function: *mut ::c_void, // Actually a function pointer
877+
pub _attribute: *mut ::pthread_attr_t,
878+
}
879+
874880
pub struct __c_anonymous_stailq_entry_devstat {
875881
pub stqe_next: *mut devstat,
876882
}
@@ -990,6 +996,21 @@ s! {
990996
pub _flags: u32,
991997
pub _clockid: u32,
992998
}
999+
1000+
// When sigevent was first added to libc, Rust still didn't support unions.
1001+
// So the definition only included one of the union's member. This
1002+
// structure exists for backwards-compatibility with consumers that still
1003+
// try to access that one member.
1004+
#[doc(hidden)]
1005+
pub struct sigevent_0_2_126 {
1006+
pub sigev_notify: ::c_int,
1007+
pub sigev_signo: ::c_int,
1008+
pub sigev_value: ::sigval,
1009+
pub sigev_notify_thread_id: ::lwpid_t,
1010+
#[cfg(target_pointer_width = "64")]
1011+
__unused1: ::c_int,
1012+
__unused2: [::c_long; 7]
1013+
}
9931014
}
9941015

9951016
s_no_extra_traits! {
@@ -1040,16 +1061,22 @@ s_no_extra_traits! {
10401061
__reserved: [::c_long; 4]
10411062
}
10421063

1064+
// Can't correctly impl Debug for unions
1065+
#[allow(missing_debug_implementations)]
1066+
#[cfg(libc_union)]
1067+
pub union __c_anonymous_sigev_un {
1068+
pub _threadid: ::__lwpid_t,
1069+
pub _sigev_thread: __c_anonymous_sigev_thread,
1070+
pub _kevent_flags: ::c_ushort,
1071+
__spare__: [::c_long; 8],
1072+
}
1073+
1074+
#[cfg(libc_union)]
10431075
pub struct sigevent {
10441076
pub sigev_notify: ::c_int,
10451077
pub sigev_signo: ::c_int,
10461078
pub sigev_value: ::sigval,
1047-
//The rest of the structure is actually a union. We expose only
1048-
//sigev_notify_thread_id because it's the most useful union member.
1049-
pub sigev_notify_thread_id: ::lwpid_t,
1050-
#[cfg(target_pointer_width = "64")]
1051-
__unused1: ::c_int,
1052-
__unused2: [::c_long; 7]
1079+
pub _sigev_un: __c_anonymous_sigev_un,
10531080
}
10541081

10551082
pub struct ptsstat {
@@ -1202,6 +1229,20 @@ s_no_extra_traits! {
12021229
}
12031230
}
12041231

1232+
impl core::ops::Deref for sigevent {
1233+
type Target = sigevent_0_2_126;
1234+
1235+
fn deref(&self) -> &Self::Target {
1236+
unsafe { &*(self as *const Self as *const sigevent_0_2_126) }
1237+
}
1238+
}
1239+
1240+
impl core::ops::DerefMut for sigevent {
1241+
fn deref_mut(&mut self) -> &mut Self::Target {
1242+
unsafe { &mut *(self as *mut Self as *mut sigevent_0_2_126) }
1243+
}
1244+
}
1245+
12051246
cfg_if! {
12061247
if #[cfg(feature = "extra_traits")] {
12071248
impl PartialEq for utmpx {
@@ -1396,28 +1437,62 @@ cfg_if! {
13961437
self.sigev_notify == other.sigev_notify
13971438
&& self.sigev_signo == other.sigev_signo
13981439
&& self.sigev_value == other.sigev_value
1399-
&& self.sigev_notify_thread_id
1400-
== other.sigev_notify_thread_id
1440+
// sigev_notify indicates which union fields are valid
1441+
&& match self.sigev_notify {
1442+
::SIGEV_NONE => true,
1443+
::SIGEV_SIGNAL => true,
1444+
::SIGEV_THREAD => unsafe {
1445+
self._sigev_un._sigev_thread
1446+
== other._sigev_un._sigev_thread
1447+
},
1448+
::SIGEV_KEVENT => unsafe {
1449+
self._sigev_un._kevent_flags
1450+
== other._sigev_un._kevent_flags
1451+
},
1452+
::SIGEV_THREAD_ID => unsafe {
1453+
self._sigev_un._threadid
1454+
== other._sigev_un._threadid
1455+
},
1456+
_ => false
1457+
}
14011458
}
14021459
}
14031460
impl Eq for sigevent {}
14041461
impl ::fmt::Debug for sigevent {
14051462
fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
1406-
f.debug_struct("sigevent")
1407-
.field("sigev_notify", &self.sigev_notify)
1408-
.field("sigev_signo", &self.sigev_signo)
1409-
.field("sigev_value", &self.sigev_value)
1410-
.field("sigev_notify_thread_id",
1411-
&self.sigev_notify_thread_id)
1412-
.finish()
1463+
let mut ds = f.debug_struct("sigevent");
1464+
ds.field("sigev_notify", &self.sigev_notify);
1465+
ds.field("sigev_signo", &self.sigev_signo);
1466+
ds.field("sigev_value", &self.sigev_value);
1467+
// The sigev_notify field indicates which union fields are valid
1468+
unsafe {
1469+
match self.sigev_notify {
1470+
::SIGEV_THREAD => ds.field("_sigev_thread",
1471+
&self._sigev_un._sigev_thread),
1472+
::SIGEV_KEVENT => ds.field("_kevent_flags",
1473+
&self._sigev_un._kevent_flags),
1474+
::SIGEV_THREAD_ID => ds.field("_threadid",
1475+
&self._sigev_un._threadid),
1476+
_ => &mut ds
1477+
}
1478+
};
1479+
ds.finish()
14131480
}
14141481
}
14151482
impl ::hash::Hash for sigevent {
14161483
fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
14171484
self.sigev_notify.hash(state);
14181485
self.sigev_signo.hash(state);
14191486
self.sigev_value.hash(state);
1420-
self.sigev_notify_thread_id.hash(state);
1487+
// The sigev_notify field indicates which union fields are valid
1488+
unsafe {
1489+
match self.sigev_notify {
1490+
::SIGEV_THREAD => self._sigev_un._sigev_thread.hash(state),
1491+
::SIGEV_KEVENT => self._sigev_un._kevent_flags.hash(state),
1492+
::SIGEV_THREAD_ID => self._sigev_un._threadid.hash(state),
1493+
_ => ()
1494+
};
1495+
}
14211496
}
14221497
}
14231498

0 commit comments

Comments
 (0)