Skip to content

Commit cc73638

Browse files
authored
Allow setting kevent_flags on struct sigevent (#1731)
* Allow setting kevent_flags on struct sigevent Also, disallow using SigevNotify::SigevThreadId on musl. I don't think it ever worked. Depends on rust-lang/libc#2813 Blocks tokio-rs/tokio#4728 * Inline libc::sigevent Because the PR to libc is stalled for over one year, with no sign of progress.
1 parent 183be1b commit cc73638

File tree

1 file changed

+211
-49
lines changed

1 file changed

+211
-49
lines changed

src/sys/signal.rs

Lines changed: 211 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,7 +1022,7 @@ feature! {
10221022
#[cfg(target_os = "freebsd")]
10231023
pub type type_of_thread_id = libc::lwpid_t;
10241024
/// Identifies a thread for [`SigevNotify::SigevThreadId`]
1025-
#[cfg(target_os = "linux")]
1025+
#[cfg(any(target_env = "gnu", target_env = "uclibc"))]
10261026
pub type type_of_thread_id = libc::pid_t;
10271027

10281028
/// Specifies the notification method used by a [`SigEvent`]
@@ -1042,8 +1042,7 @@ pub enum SigevNotify {
10421042
/// structure of the queued signal.
10431043
si_value: libc::intptr_t
10441044
},
1045-
// Note: SIGEV_THREAD is not implemented because libc::sigevent does not
1046-
// expose a way to set the union members needed by SIGEV_THREAD.
1045+
// Note: SIGEV_THREAD is not implemented, but could be if desired.
10471046
/// Notify by delivering an event to a kqueue.
10481047
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
10491048
#[cfg_attr(docsrs, doc(cfg(all())))]
@@ -1053,8 +1052,24 @@ pub enum SigevNotify {
10531052
/// Will be contained in the kevent's `udata` field.
10541053
udata: libc::intptr_t
10551054
},
1055+
/// Notify by delivering an event to a kqueue, with optional event flags set
1056+
#[cfg(target_os = "freebsd")]
1057+
#[cfg_attr(docsrs, doc(cfg(all())))]
1058+
#[cfg(feature = "event")]
1059+
SigevKeventFlags {
1060+
/// File descriptor of the kqueue to notify.
1061+
kq: RawFd,
1062+
/// Will be contained in the kevent's `udata` field.
1063+
udata: libc::intptr_t,
1064+
/// Flags that will be set on the delivered event. See `kevent(2)`.
1065+
flags: crate::sys::event::EventFlag
1066+
},
10561067
/// Notify by delivering a signal to a thread.
1057-
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
1068+
#[cfg(any(
1069+
target_os = "freebsd",
1070+
target_env = "gnu",
1071+
target_env = "uclibc",
1072+
))]
10581073
#[cfg_attr(docsrs, doc(cfg(all())))]
10591074
SigevThreadId {
10601075
/// Signal to send
@@ -1079,17 +1094,139 @@ mod sigevent {
10791094
#![any(feature = "aio", feature = "signal")]
10801095

10811096
use std::mem;
1082-
use std::ptr;
10831097
use super::SigevNotify;
1084-
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
1085-
use super::type_of_thread_id;
1098+
1099+
#[cfg(target_os = "freebsd")]
1100+
pub(crate) use ffi::sigevent as libc_sigevent;
1101+
#[cfg(not(target_os = "freebsd"))]
1102+
pub(crate) use libc::sigevent as libc_sigevent;
1103+
1104+
// For FreeBSD only, we define the C structure here. Because the structure
1105+
// defined in libc isn't correct. The real sigevent contains union fields,
1106+
// but libc could not represent those when sigevent was originally added, so
1107+
// instead libc simply defined the most useful field. Now that Rust can
1108+
// represent unions, there's a PR to libc to fix it. However, it's stuck
1109+
// forever due to backwards compatibility concerns. Even though there's a
1110+
// workaround, libc refuses to merge it. I think it's just too complicated
1111+
// for them to want to think about right now, because that project is
1112+
// short-staffed. So we define it here instead, so we won't have to wait on
1113+
// libc.
1114+
// https://github.com/rust-lang/libc/pull/2813
1115+
#[cfg(target_os = "freebsd")]
1116+
mod ffi {
1117+
use std::{fmt, hash};
1118+
1119+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1120+
#[repr(C)]
1121+
pub struct __c_anonymous_sigev_thread {
1122+
pub _function: *mut libc::c_void, // Actually a function pointer
1123+
pub _attribute: *mut libc::pthread_attr_t,
1124+
}
1125+
#[derive(Clone, Copy)]
1126+
// This will never be used on its own, and its parent has a Debug impl,
1127+
// so it doesn't need one.
1128+
#[allow(missing_debug_implementations)]
1129+
#[repr(C)]
1130+
pub union __c_anonymous_sigev_un {
1131+
pub _threadid: libc::__lwpid_t,
1132+
pub _sigev_thread: __c_anonymous_sigev_thread,
1133+
pub _kevent_flags: libc::c_ushort,
1134+
__spare__: [libc::c_long; 8],
1135+
}
1136+
1137+
#[derive(Clone, Copy)]
1138+
#[repr(C)]
1139+
pub struct sigevent {
1140+
pub sigev_notify: libc::c_int,
1141+
pub sigev_signo: libc::c_int,
1142+
pub sigev_value: libc::sigval,
1143+
pub _sigev_un: __c_anonymous_sigev_un,
1144+
}
1145+
1146+
impl fmt::Debug for sigevent {
1147+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1148+
let mut ds = f.debug_struct("sigevent");
1149+
ds.field("sigev_notify", &self.sigev_notify)
1150+
.field("sigev_signo", &self.sigev_signo)
1151+
.field("sigev_value", &self.sigev_value);
1152+
// Safe because we check the sigev_notify discriminant
1153+
unsafe {
1154+
match self.sigev_notify {
1155+
libc::SIGEV_KEVENT => {
1156+
ds.field("sigev_notify_kevent_flags", &self._sigev_un._kevent_flags);
1157+
}
1158+
libc::SIGEV_THREAD_ID => {
1159+
ds.field("sigev_notify_thread_id", &self._sigev_un._threadid);
1160+
}
1161+
libc::SIGEV_THREAD => {
1162+
ds.field("sigev_notify_function", &self._sigev_un._sigev_thread._function);
1163+
ds.field("sigev_notify_attributes", &self._sigev_un._sigev_thread._attribute);
1164+
}
1165+
_ => ()
1166+
};
1167+
}
1168+
ds.finish()
1169+
}
1170+
}
1171+
1172+
impl PartialEq for sigevent {
1173+
fn eq(&self, other: &Self) -> bool {
1174+
let mut equals = self.sigev_notify == other.sigev_notify;
1175+
equals &= self.sigev_signo == other.sigev_signo;
1176+
equals &= self.sigev_value == other.sigev_value;
1177+
// Safe because we check the sigev_notify discriminant
1178+
unsafe {
1179+
match self.sigev_notify {
1180+
libc::SIGEV_KEVENT => {
1181+
equals &= self._sigev_un._kevent_flags == other._sigev_un._kevent_flags;
1182+
}
1183+
libc::SIGEV_THREAD_ID => {
1184+
equals &= self._sigev_un._threadid == other._sigev_un._threadid;
1185+
}
1186+
libc::SIGEV_THREAD => {
1187+
equals &= self._sigev_un._sigev_thread == other._sigev_un._sigev_thread;
1188+
}
1189+
_ => /* The union field is don't care */ ()
1190+
}
1191+
}
1192+
equals
1193+
}
1194+
}
1195+
1196+
impl Eq for sigevent {}
1197+
1198+
impl hash::Hash for sigevent {
1199+
fn hash<H: hash::Hasher>(&self, s: &mut H) {
1200+
self.sigev_notify.hash(s);
1201+
self.sigev_signo.hash(s);
1202+
self.sigev_value.hash(s);
1203+
// Safe because we check the sigev_notify discriminant
1204+
unsafe {
1205+
match self.sigev_notify {
1206+
libc::SIGEV_KEVENT => {
1207+
self._sigev_un._kevent_flags.hash(s);
1208+
}
1209+
libc::SIGEV_THREAD_ID => {
1210+
self._sigev_un._threadid.hash(s);
1211+
}
1212+
libc::SIGEV_THREAD => {
1213+
self._sigev_un._sigev_thread.hash(s);
1214+
}
1215+
_ => /* The union field is don't care */ ()
1216+
}
1217+
}
1218+
}
1219+
}
1220+
}
10861221

10871222
/// Used to request asynchronous notification of the completion of certain
10881223
/// events, such as POSIX AIO and timers.
10891224
#[repr(C)]
1090-
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1225+
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1226+
// It can't be Copy on all platforms.
1227+
#[allow(missing_copy_implementations)]
10911228
pub struct SigEvent {
1092-
sigevent: libc::sigevent
1229+
sigevent: libc_sigevent
10931230
}
10941231

10951232
impl SigEvent {
@@ -1107,65 +1244,90 @@ mod sigevent {
11071244
/// `SIGEV_SIGNAL`. That field is part of a union that shares space with the
11081245
/// more genuinely useful `sigev_notify_thread_id`
11091246
pub fn new(sigev_notify: SigevNotify) -> SigEvent {
1110-
let mut sev = unsafe { mem::MaybeUninit::<libc::sigevent>::zeroed().assume_init() };
1111-
sev.sigev_notify = match sigev_notify {
1112-
SigevNotify::SigevNone => libc::SIGEV_NONE,
1113-
SigevNotify::SigevSignal{..} => libc::SIGEV_SIGNAL,
1247+
let mut sev: libc_sigevent = unsafe { mem::zeroed() };
1248+
match sigev_notify {
1249+
SigevNotify::SigevNone => {
1250+
sev.sigev_notify = libc::SIGEV_NONE;
1251+
},
1252+
SigevNotify::SigevSignal{signal, si_value} => {
1253+
sev.sigev_notify = libc::SIGEV_SIGNAL;
1254+
sev.sigev_signo = signal as libc::c_int;
1255+
sev.sigev_value.sival_ptr = si_value as *mut libc::c_void
1256+
},
11141257
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
1115-
SigevNotify::SigevKevent{..} => libc::SIGEV_KEVENT,
1258+
SigevNotify::SigevKevent{kq, udata} => {
1259+
sev.sigev_notify = libc::SIGEV_KEVENT;
1260+
sev.sigev_signo = kq;
1261+
sev.sigev_value.sival_ptr = udata as *mut libc::c_void;
1262+
},
11161263
#[cfg(target_os = "freebsd")]
1117-
SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID,
1118-
#[cfg(all(target_os = "linux", target_env = "gnu", not(target_arch = "mips")))]
1119-
SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID,
1120-
#[cfg(all(target_os = "linux", target_env = "uclibc", not(target_arch = "mips")))]
1121-
SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID,
1122-
#[cfg(any(all(target_os = "linux", target_env = "musl"), target_arch = "mips"))]
1123-
SigevNotify::SigevThreadId{..} => 4 // No SIGEV_THREAD_ID defined
1124-
};
1125-
sev.sigev_signo = match sigev_notify {
1126-
SigevNotify::SigevSignal{ signal, .. } => signal as libc::c_int,
1127-
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
1128-
SigevNotify::SigevKevent{ kq, ..} => kq,
1129-
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
1130-
SigevNotify::SigevThreadId{ signal, .. } => signal as libc::c_int,
1131-
_ => 0
1132-
};
1133-
sev.sigev_value.sival_ptr = match sigev_notify {
1134-
SigevNotify::SigevNone => ptr::null_mut::<libc::c_void>(),
1135-
SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut libc::c_void,
1136-
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
1137-
SigevNotify::SigevKevent{ udata, .. } => udata as *mut libc::c_void,
1138-
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
1139-
SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut libc::c_void,
1140-
};
1141-
SigEvent::set_tid(&mut sev, &sigev_notify);
1264+
#[cfg(feature = "event")]
1265+
SigevNotify::SigevKeventFlags{kq, udata, flags} => {
1266+
sev.sigev_notify = libc::SIGEV_KEVENT;
1267+
sev.sigev_signo = kq;
1268+
sev.sigev_value.sival_ptr = udata as *mut libc::c_void;
1269+
sev._sigev_un._kevent_flags = flags.bits();
1270+
},
1271+
#[cfg(target_os = "freebsd")]
1272+
SigevNotify::SigevThreadId{signal, thread_id, si_value} => {
1273+
sev.sigev_notify = libc::SIGEV_THREAD_ID;
1274+
sev.sigev_signo = signal as libc::c_int;
1275+
sev.sigev_value.sival_ptr = si_value as *mut libc::c_void;
1276+
sev._sigev_un._threadid = thread_id;
1277+
}
1278+
#[cfg(any(target_env = "gnu", target_env = "uclibc"))]
1279+
SigevNotify::SigevThreadId{signal, thread_id, si_value} => {
1280+
sev.sigev_notify = libc::SIGEV_THREAD_ID;
1281+
sev.sigev_signo = signal as libc::c_int;
1282+
sev.sigev_value.sival_ptr = si_value as *mut libc::c_void;
1283+
sev.sigev_notify_thread_id = thread_id;
1284+
}
1285+
}
11421286
SigEvent{sigevent: sev}
11431287
}
11441288

1145-
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
1146-
fn set_tid(sev: &mut libc::sigevent, sigev_notify: &SigevNotify) {
1147-
sev.sigev_notify_thread_id = match *sigev_notify {
1148-
SigevNotify::SigevThreadId { thread_id, .. } => thread_id,
1149-
_ => 0 as type_of_thread_id
1150-
};
1151-
}
1152-
1153-
#[cfg(not(any(target_os = "freebsd", target_os = "linux")))]
1154-
fn set_tid(_sev: &mut libc::sigevent, _sigev_notify: &SigevNotify) {
1289+
/// Return a copy of the inner structure
1290+
#[cfg(target_os = "freebsd")]
1291+
pub fn sigevent(&self) -> libc::sigevent {
1292+
// Safe because they're really the same structure. See
1293+
// https://github.com/rust-lang/libc/pull/2813
1294+
unsafe {
1295+
mem::transmute::<libc_sigevent, libc::sigevent>(self.sigevent)
1296+
}
11551297
}
11561298

11571299
/// Return a copy of the inner structure
1300+
#[cfg(not(target_os = "freebsd"))]
11581301
pub fn sigevent(&self) -> libc::sigevent {
11591302
self.sigevent
11601303
}
11611304

11621305
/// Returns a mutable pointer to the `sigevent` wrapped by `self`
1306+
#[cfg(target_os = "freebsd")]
1307+
pub fn as_mut_ptr(&mut self) -> *mut libc::sigevent {
1308+
// Safe because they're really the same structure. See
1309+
// https://github.com/rust-lang/libc/pull/2813
1310+
&mut self.sigevent as *mut libc_sigevent as *mut libc::sigevent
1311+
}
1312+
1313+
/// Returns a mutable pointer to the `sigevent` wrapped by `self`
1314+
#[cfg(not(target_os = "freebsd"))]
11631315
pub fn as_mut_ptr(&mut self) -> *mut libc::sigevent {
11641316
&mut self.sigevent
11651317
}
11661318
}
11671319

11681320
impl<'a> From<&'a libc::sigevent> for SigEvent {
1321+
#[cfg(target_os = "freebsd")]
1322+
fn from(sigevent: &libc::sigevent) -> Self {
1323+
// Safe because they're really the same structure. See
1324+
// https://github.com/rust-lang/libc/pull/2813
1325+
let sigevent = unsafe {
1326+
mem::transmute::<libc::sigevent, libc_sigevent>(*sigevent)
1327+
};
1328+
SigEvent{ sigevent }
1329+
}
1330+
#[cfg(not(target_os = "freebsd"))]
11691331
fn from(sigevent: &libc::sigevent) -> Self {
11701332
SigEvent{ sigevent: *sigevent }
11711333
}

0 commit comments

Comments
 (0)