Skip to content
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

implement signal BitOr and SigSet::from! #615

Closed
wants to merge 5 commits into from
Closed

implement signal BitOr and SigSet::from! #615

wants to merge 5 commits into from

Conversation

cyphar
Copy link

@cyphar cyphar commented Jun 3, 2017

This allows for far more idiomatic initialisation of SigSet without
having to declare mut variables, as well as mirroring the vec![]
semantics (though arguably SigSet is closer to a bitfield than a
vector).

let mut sigset = SigSet::empty();
sigset.add(Signal::SIGINT);
// versus
let sigset = sigset![Signal::SIGINT];

And the BitOr* operations futher make it much easier to idiomatically
work with SigSet and Signal, without having to use sigset![] and
SigSet::extend() everywhere.

let sigset_a = sigset![Signal::SIGINT];
let sigset_b = sigset![Signal::SIGTERM];
let mut sigset = SigSet::empty();
sigset.extend(&sigset_a);
sigset.extend(&sigset_b);
// or
let sigset = sigset![Signal::SIGINT, Signal::SIGTERM];
// versus
let sigset = Signal::SIGINT | Signal::SIGTERM;

Implements #609
Signed-off-by: Aleksa Sarai [email protected]

@Susurrus
Copy link
Contributor

Susurrus commented Jun 3, 2017

I'm not a huge fan of using .into() to convert a Signal to a SigSet. They're not really equivalent.

The only use case I see missing here is supporting nice initialization. What about something akin to the vec![] macro but for SigSet?

@Susurrus
Copy link
Contributor

Susurrus commented Jun 3, 2017

Additionally I'm not a fan of using BitOr to OR together things that aren't really bitfields. I think it's semantically confusing.

@cyphar
Copy link
Author

cyphar commented Jun 3, 2017

I'm not a huge fan of using .into() to convert a Signal to a SigSet. They're not really equivalent.

I will admit that I'm very new to Rust so I'm not sure what the right semantics are (and am quite willing to learn on what the "right way" of defining interfaces is in the Rust ecosystem). Is std::convert::From::from similarly "bad"?

I think it's semantically confusing.

While the underyling interface isn't a bitfield (and is accessed through the super-clumsy libc interfaces), a SigSet is semantically a set so I'm not sure I agree that set operations shouldn't be defined on something that is a set. I don't know if you use use Python, but Python sets define unary operations such as | and so on. It's unfortunate that Rust called the trait BitOr because | is quite useful for other things 😉.

@cyphar
Copy link
Author

cyphar commented Jun 3, 2017

What about something akin to the vec![] macro but for SigSet?

That could also work I guess, though now users will have to use #[macro_use].

@Susurrus
Copy link
Contributor

Susurrus commented Jun 4, 2017

I will admit that I'm very new to Rust so I'm not sure what the right semantics are (and am quite willing to learn on what the "right way" of defining interfaces is in the Rust ecosystem). Is std::convert::From::from similarly "bad"?

From and Into normally mean that the types are roughly equivalent. Like a RawFd and a u32, the datatypes are similar semantically. With a Signal and a SigSet, I don't like that you can into(), as they aren't equivalent. One is a container of another. I see it like how I don't think you should implement u32::Into<Vec<u32>> since I don't consider those equivalent. One is effectively a container and the other is an element.

Additionally the value of Into seems quite minimal. Your bitor and bitor_assign tests cover all use cases I can imagine and that you have provided.

One thing I'd like to see is that SigSet get some expanded documentation describing these various ways of initializing them like you mention in this comment thread. That should make sure that future users don't get confused on how to best generate a SigSet.

This is looking really good though, that's for taking this on!

@cyphar
Copy link
Author

cyphar commented Jun 5, 2017

@Susurrus Done, PTAL?

@cyphar cyphar changed the title implement signal BitOr and SigSet::from implement signal BitOr and sigset! Jun 5, 2017
#[derive(Clone, Copy)]
pub struct SigSet {
sigset: libc::sigset_t
}

/// sigset is a macro that allows for far more idiomatic initalisation of SigSet. Note that while
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should have a sigset![] macro at all. I don't see what it offers that the BitOr operator doesn't allow. And the ergonomics with macros in Rust right now are less than that of regular functions or operators. Let's go ahead and just remove it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The downside is that it requires doing SigSet::empty() | Signal::SIGHUP in order to "promote" a Signal to a SigSet.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's a great point. I'm actually thinking that going back to implementing From<Signal> for SigSet to be the right solution. Then we can rewrite all functions that take a SigSet to instead take a T: Into<SigSet> instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, From<Signal> for SigSet is a very reasonable thing to do IMO here. Agreed that it would be better to not have that macro unless it is really required.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I'll go back to that. Do you want me to do the T: Into<SigSet> here or in a separate patchset?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same patchset. It's fine to have the From impl and the conversion of functions to use it as another commit.


// Currently there is only one definition of c_int in libc, as well as only one
// type for signal constants.
// We would prefer to use the libc::c_int alias in the repr attribute. Unfortunately
// this is not (yet) possible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this line.

Also, Signal needs doc comments now that there's this new behavior where you can combine Signals into SigSets.

@cyphar
Copy link
Author

cyphar commented Jun 6, 2017

Alright, I've updated this to have impl From<Signal> for SigSet. I've also added the Into<SigSet>, and I fixed the signalfd tests and documentation (which used to not even compile).

@cyphar
Copy link
Author

cyphar commented Jun 6, 2017

I'm sad that you can't implement a trait for a generic type. It would be nice to be able to do

impl<T: Into<SigSet>> BitOr<T> for T { /* ... */ }

@cyphar cyphar changed the title implement signal BitOr and sigset! implement signal BitOr and SigSet::from! Jun 6, 2017
///
/// ```
/// # use ::nix::sys::signal::*;
/// // Into and From promote a signal to a SigSet that has only the given signal inside it.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please leave these comments out of the code example instead and separate these into two separate examples. It'll make it slightly easier reading. Something like:

/// Into and From promote a signal to a SigSet that has only the given signal inside it.
/// ```
/// # use ::nix::sys::signal::*;
/// let sigset_a: SigSet = Signal::SIGTERM.into();
/// let sigset_b = SigSet::from(Signal::SIGTERM);
///```
///
/// `SigSet`s of multiple signals can be created by using the `|` (`BitOr`) operator:
/// ```
/// # use ::nix::sys::signal::*;
/// let sigset = Signal::SIGTERM | Signal::SIGHUP;
/// ```
///
/// Existing `SigSet`s can be extended using the `|` (`BitOr`) or `|=` (`BitAssign`) operator as well:
/// ```
/// let mut sigset = SigSet::empty();
/// sigset |= Signal::SIGQUIT;
/// ```

@@ -218,11 +263,42 @@ pub enum SigmaskHow {
SIG_SETMASK = libc::SIG_SETMASK,
}

/// SigSet represents a set of signals and is a thin wrapper around the libc APIs for sigset_t.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put all types in "`" like sigset_t, etc.

/// # Examples
///
/// ```
/// # use ::nix::sys::signal::*;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please simplify this example to one SigSet. Maybe you should just copy/paste the relevant examples from the Signal docs above.

/// let sigset_d = sigset_c | sigset_a;
/// ```
///
/// The above would traditionally require far more finicking with SigSet::add, SigSet::extend and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the first sentence here doesn't add anything, let's remove it.

@@ -60,10 +60,10 @@ pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd> {
/// # Examples
///
/// ```
/// use nix::sys::signal::*;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make these use lines not appear in the docs by prepending with # since we're modifying this example.

@@ -11,8 +11,7 @@ use nix::unistd;
fn main() {
print!("test test_signalfd ... ");

let mut mask = signal::SigSet::empty();
mask.add(signal::SIGUSR1).unwrap();
let mask: signal::SigSet = signal::SIGUSR1.into();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should allow a Signal to be passed directly into SignalFd::new() and related functions. Could you add that?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to figure out how to do this and I've bumped up against the edges of my knowledge of Rust. Because SignalFd::new (and the other various methods for SignalFd) take &SigSet, we can't easily do something like this:

let sigmask = Signal::SIGTERM;
SignalFd::new(sigmask);
// or
SignalFd::new(&sigmask);

impl From<&Signal> for &SigSet won't work because of trait specialization not being implementated, but there's an even deeper issue. I was trying to get impl AsRef<SigSet> for Signal but realised that it doesn't appear to be possible (given Rust's borrowing model) to return an immutable reference to something allocated in the function that you're returning from. As a simple example, this simply doesn't work for obvious reasons:

impl AsRef<SigSet> for Signal {
    fn as_ref(&self) -> &SigSet {
        &self.clone().into()
    }
}

Is there a way to make that work? It seems quite silly that you can't convert &Into<T> into Into<&T> but there's probably some much harder problem here than I'm seeing.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In particular, I'm not really convinced this particular characterbake makes sense:

pub fn signalfd<'a, T: Into<&'a SigSet>>(fd: RawFd, mask: T, flags: SfdFlags) -> Result<RawFd>

In particular, I'm not sure what will actually fulfill the bound Into<&'a SigSet> other than &SigSet -- making it just a waste of characters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That'd a good point. My question now is why doesn't ppoll take a &SigSet instead of by-value?

CHANGELOG.md Outdated
@@ -16,6 +16,13 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#551](https://github.com/nix-rust/nix/pull/551))
- Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}`
([#556](https://github.com/nix-rust/nix/pull/556)
- Added `nix::sys::SigSet::From<Signal>` and refactored some APIs to allow
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a change, it should be put down in the Changed section.

@Susurrus
Copy link
Contributor

Susurrus commented Jun 6, 2017

Getting close now. Some documentation cleanups and a few more functions to convert to use Into<SigSet> and then this should be finished.

@Susurrus
Copy link
Contributor

@cyphar You still interested in pushing this over the finish line?

@cyphar
Copy link
Author

cyphar commented Jun 28, 2017

Yes. The past few weeks have been my finals of this semester of university, so I've not been able to touch this. I'll rebase and fix up your comments in the coming days. Thanks.

@Susurrus
Copy link
Contributor

@cyphar I'd love to get this in the next release of nix. Will you have time coming up here to rebase this so we can get this merged?

@cyphar
Copy link
Author

cyphar commented Jul 16, 2017

@Susurrus Sorry for the delays, other projects have been getting close to release. I've rebased, but we still haven't figured out how to handle #615 (comment). Should I just remove my Into<&SigSet> changes? And what about ppoll?

cyphar added 5 commits July 23, 2017 15:59
The normal libc::sigset_t API is incredibly clunky when initialising
SigSets. Since a SigSet semantically is a (mathematical) set of Signals,
it makes sense to allow Signals to be promoted to a SigSet. This will
also allow us to change our API to take Into<SigSet> rather than SigSet,
making this even more idiomatic.

  let mut sigset = SigSet::empty();
  sigset.add(Signal::SIGINT);
  // versus
  let sigset: SigSet = Signal::SIGINT.into();
  let sigset = SigSet::from(Signal::SIGINT);

Signed-off-by: Aleksa Sarai <[email protected]>
In order to make operating on libc::sigset_t even more idiomatic, define
several BitOr (and BitOrAssign) implementations. This makes it possible
to operate on SigSet much more easily without needing to use
SigSet::extend() everywhere.

  let sigset_a = SigSet::from(Signal::SIGINT);
  let sigset_b = SigSet::from(Signal::SIGTERM);
  let mut sigset_c = SigSet::empty();
  sigset_c.extend(&sigset_a);
  sigset_c.extend(&sigset_b);
  // versus
  let sigset = Signal::SIGINT | Signal::SIGTERM;

Signed-off-by: Aleksa Sarai <[email protected]>
Since we now have From<SigSet> and Into<SigSet>::BitOr, we can make the
SigSet API (and also internal usage) far easier to read and more
idiomatic.

Signed-off-by: Aleksa Sarai <[email protected]>
The old documentation and tests wouldn't compile due to using an old nix
API. We probably should enable signalfd building in CI (which is the
reason this wasn't caught).

Signed-off-by: Aleksa Sarai <[email protected]>
@cyphar
Copy link
Author

cyphar commented Jul 23, 2017

Rebased again.

@Susurrus
Copy link
Contributor

Susurrus commented Aug 1, 2017

This needs another rebase (be sure to move the CHANGELOG entries into the new unreleased section) and there are some tests failing that need to be fixed.

@Susurrus
Copy link
Contributor

@cyphar Can you rebase this and fix the tests that were failing previously?

@Susurrus
Copy link
Contributor

Susurrus commented Dec 4, 2017

There's been no update in almost 5 months, so I'm going to go ahead and close this PR.

@Susurrus Susurrus closed this Dec 4, 2017
@cyphar
Copy link
Author

cyphar commented Dec 4, 2017

I didn't have a chance to rebase it because there was still the question about &SigSet which wasn't resolved, and I didn't see the benefit of rebasing it without resolving that problem first. If you want, I can rebase and create a new PR, but we should figure out what the right solution is for signalfd.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants