vorner / signal-hook

Rust library allowing to register multiple handlers for the same signal

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Signals::forever should not consume

vorner opened this issue · comments

There's no actual reason to consume, as repeatedly calling wait is possible on &mut reference. Also, consuming makes it hard to use after a for with break in it.

The tricky thing is the difference between the signal_hook::iterator module and the adapter crates. The former is happy with a mutable reference to the SignalDelivery in the SignalIterator struct because the owning Signal instance outlives the iterator.

The latter create the SignalDelivery struct in their constructor and pass it on to the SignalIterator. In this case the SignalIterator can't just hold a reference but must own the SignalDelivery instance.

But I figured out I could make the SignalIterator more generic and just rely on the BorrowMut trait to achieve both. I already had a working solution but forgot to update my local copy with your latest upstream changes. So I have to do those changes again tomorrow 😅

But I think this will be another breaking change because users of the blocking iterator might have to add a mut to their instance and also use a &mut at the point of iteration.

Thanks for dropping in. I know this would be breaking, but I'm mulling over some more breaking changes anyway ‒ some kind of restructuring of modules, and few more things.

But I'm not going to do them right away. So no hurry about this one ‒ if you have the code, it would be nice to have it around, but I'm not sure I want to merge that breaking change into master just yet.

The latter create the SignalDelivery struct in their constructor and pass it on to the SignalIterator. In this case the SignalIterator can't just hold a reference but must own the SignalDelivery instance.

On a second thought, what is the reason why it can't be a reference and must outlive/must be static? With async/await support, futures and stream that borrow something should be perfectly fine, shouldn't they?

The blocking iterator has a two step approach: First constructing the Signals instance and then getting the iterator through Signals::forever. We decided against a two step construction approach for the async crates because it offers no advantages. Instead the constructor methods create a SignalDelivery instance and pass it to the wrapped SignalIterator. E. g. from the async_std crate:

pub fn with_exfiltrator<I, S>(signals: I, exfiltrator: E) -> Result<Self, Error> {
    let (read, write) = UnixStream::pair()?;
    let inner = SignalDelivery::with_pipe(read, write, exfiltrator, signals)?;
    Ok(Self(SignalIterator::new(inner)))
}

The lifetime of SignalDelivery will be limited to the function call if you do not return it from / move it out of the function. But moving a value isn't possible if there are any outstanding references to it because the memory location the reference is pointing would be invalid after moving. This is why SignalIterator can't take a reference in this case. At least not without something fancy like a self referential struct.