-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provide a way to const-construct a sem_t
#2015
Comments
POSIX allows semaphores to be implemented using file descriptors. Unless I'm missing something, this means that there's no portable way of doing |
You would still need to call |
What's wrong with this? static mut sem: MaybeUninit<libc::sem_t> = MaybeUninit::uninit(); And to initialize it you use: let ret = unsafe { libc::sem_init(sem.as_mut_ptr(), 0, 0) }; Obviously, it's not very nice to use, but I don't think that anything better can be provided, because |
That is UB as soon as you have more than one thread accessing static mut sem: UnsafeCell<MaybeUninit<libc::sem_t>> = UnsafeCell::new(MaybeUninit::uninit()); (Note: Furthermore, you cannot use the methods on I don't think I need to explain why this is terrible... |
@Diggsey It sounds like you actually want a lazy_static! {
static ref SEMAPHORE: libc::sem_t = {
let mut semaphore: MaybeUninit<libc::sem_t> = MaybeUninit::uninit();
libc::sem_init(semaphore.as_mut_ptr(), /*other args*/);
unsafe {semaphore.assume_init()}
};
}
Hope that helps. |
@maxbla No, that is just a hacky workaround that won't suffice for my use-case (accessing the semaphore from a signal handler). Only certain operations on a There's no reason why |
Yeah, rust doesn't generally play nicely with async signal safety. Luckily, transmute is const, so I think you can still accomplish what you're after without any additions to // AlignedSem has the same alignment and size as a sem_t
#[repr(C)]
union AlignedSem {
array: [u8; size_of::<libc::sem_t>()],
sem: libc::sem_t,
}
// particularly unsafe - subverts detection of use-before-initialize bugs
static mut MUTEX: libc::sem_t = unsafe {
std::mem::transmute(AlignedSem{array: [0u8; size_of::<libc::sem_t>()]})
};
fn main() {
unsafe {libc::sem_init(&mut MUTEX as *mut _, 0, 1)};
// register signal handlers here
} Rust really makes this hard - the need for the AlignedSem type is unfortunate. |
As mentioned, creating a mutable borrow to the static variable is UB, since it can be accessed by multiple threads. |
I did this
I made a const, zeroed
As I constructed it, that mutable borrow was the first line in main. As long as you call sem_init() before any threads are spawned, UB is not possible. Hey, while you're at the start of main, make a let mutex_ptr = unsafe {&mut MUTEX as *mut _};
// using mutex_ptr might alias *mut, but that is allowed
let res = unsafe{libc::sem_init(mutex_ptr, 0, 1)}; sem_post() is async signal safe, so you can safely clone Alternatively, use a It sounds like everything you asked for is possible, just the specific examples of code given to you were not suitable for your use case. If you either wrote a minimal example of what you're doing in C or provided an example of an api libc could add that would do what you need, this issue would be actionable. |
@maxbla there is a workaround, which I presented here: #2015 (comment) The idea of storing the That said, the fact that these workarounds are so obscure, and that it's so easy to accidentally get UB, is reason enough to add a const-constructor to Furthermore, |
mutex_ptr can't be static since it isn't // AlignedSem has the same alignment and size as a sem_t
#[repr(C)]
union AlignedSem {
array: [u8; size_of::<libc::sem_t>()],
sem: libc::sem_t,
}
// SAFETY: see std::mem::zeroed()
static mut MUTEX: UnsafeCell<libc::sem_t> = unsafe {
let zeroed_sem = std::mem::transmute(AlignedSem{array: [0u8; size_of::<libc::sem_t>()]});
UnsafeCell::new(zeroed_sem)
};
fn main() {
unsafe {libc::sem_init(MUTEX.get(), 0, 1)};
// register signal handlers here
}
libc is supposed to be a low-level binding to system types, constants, and functions. The fact that it provides the same functionality is merely a consequence of this. For example, C doesn't have a function to statically zero memory, it has a language feature - that all global and static variables are automatically initialized to zero, but also
If you're writing signal handlers (in Rust or C), you need some obscure knowledge. Hopefully std::MaybeUninit::zeroed becomes const soon and the required knowledge becomes less obscure. For now, no other type in this crate has its own special special const constructor and I don't see what makes sem_t special. This seems more like the territory of nix. |
Luckily
True, but sometimes language limitations require library-level solutions. TBH, I would be happy if all types defined by libc had a |
I think this should be closed in favor of rust-lang/rust#62061 |
Some bit of self-ad. |
@DoumanAsh Thanks for your self-ad semaphore, however it's a local one not inter-process, will you add an inter-process one? |
In fact it is const since Rust 1.75. :) |
(
mem::zeroed()
is never going to be const, andMaybeUninit::zeroed().assume_init()
is still not const)The text was updated successfully, but these errors were encountered: