-
Notifications
You must be signed in to change notification settings - Fork 313
ref(iroh): Relay map extend #3577
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
base: main
Are you sure you want to change the base?
Conversation
flub
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for writing the example in the extend docs! It's really nice to have a trait impl documented, should be done more often! Also really helpful here because of the awkward ::Vec<_> bit.
| /// Extends this `RelayMap` with another one. | ||
| pub fn extend(&self, other: &RelayMap) { | ||
| /// Joins this `RelayMap` with another one into a new one | ||
| pub fn join(self, other: RelayMap) -> RelayMap { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this called chain usually? I think this is very similar to the Iterator::chain signature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it is. I opted for join because a Map is not something that can (semantically) be put into a ⛓️ chain! 😉 I think of chain() something that can be applied to a sequence of something (hence Iterator::chain but not BTreeMap::chain for example).
I have no strong feelings about the actual method name tho, so if you want me to change it, I'll happily do so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not convinced passing owned versions makes sense for a fully clonable thing. This API really doesn't communicate the fact that this doesn't actually create a new version, but rather modifies the underlying, likely shared instances
iroh-relay/src/relay_map.rs
Outdated
| pub fn extend(&self, other: &RelayMap) { | ||
| /// Joins this `RelayMap` with another one into a new one | ||
| pub fn join(self, other: RelayMap) -> RelayMap { | ||
| // SAFETY: We have exclusive access to this object |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I appreciate documenting this, though I think this is slightly incorrect:
- SAFETY is used to document
unsafecode, panicking is not unsafe (tokio is full of panicking functions!) - This is not about exclusive access, the assertion here is that the lock is not poisoned. That is, there was no panic while holding the lock. The reasoning is that if there was a panic while holding the lock the internal invariants that the lock protects may not be upheld.
But writing that reasoning out, you can probably avoid panicking here I think. Because there are very simple internal variants you can probably just extract the internal state from the poisoned lock and put it back in there. It does require you to audit all the write paths to make sure they don't contain panicking code themselves though, because otherwise you silently lose some state. But all the write paths are internal via the functions so that's possible. It's a bunch of work for marginal gain 😄 but you can get the ideal of no panics. (Which has some small benefits to generated code size IIRC, but we've never really started optimising for this yet).
Anyway, the panics on poison are perfectly fine as well :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SAFETY is used to document unsafe code, panicking is not unsafe (tokio is full of panicking functions!)
True. Maybe // This is safe because... rather?
This is not about exclusive access, the assertion here is that the lock is not poisoned. [...]
Also true. I need to change that.
About your last bit: Yes, but that's probably not worth the effort.
|
Added some fixups for the misleading comment. |
|
@matthiasbeyer we are doing a release on monday! Would love to get this in if you could fix the failing CI. |
Signed-off-by: Matthias Beyer <[email protected]>
Signed-off-by: Matthias Beyer <[email protected]>
93d4072 to
6edd6dc
Compare
|
This should suffice. I can fix issues until tomorrow, Saturday, around 13:00 UTC. After that I am enjoying Hans Zimmer and on Sunday I will be traveling 😉 👍 |
| /// # let relay_map_a: RelayMap = { unimplemented!() }; | ||
| /// # let relay_map_b: RelayMap = { unimplemented!() }; | ||
| /// | ||
| /// relay_map_a.extend(relay_map_b.relays::<Vec<_>>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is less efficient, and seems rather pointless to support, if you have to collect & allocate into a vector first before using the trait
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inner lock seems to stop you from doing anything nice using IntoIterator or Extend. 😞
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, that's the issue yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this would be a DashMap, I guess it would work.
That also would mean that not the whole map must be locked when adding/removing.
But it introduces other things that are dangerous, such as holding dashmap locks over await points (which can be linted against though).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do you lint against that? I didn't know that was possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But clippy can do that (by now) via the await-holding-invalid-types lint: https://rust-lang.github.io/rust-clippy/stable/index.html#await_holding_invalid_type
flub
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the main objection to the current API is that extend overlaps with Extend::extend? But implementing the trait is really ugly due to the internal locks.
The existing API does guide you to do the right thing, which really is what matters. I'm not sure supporting the Extend trait directly does improve ergonomics. So overall I think I'm -1 on this so far.
| /// # let relay_map_a: RelayMap = { unimplemented!() }; | ||
| /// # let relay_map_b: RelayMap = { unimplemented!() }; | ||
| /// | ||
| /// relay_map_a.extend(relay_map_b.relays::<Vec<_>>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inner lock seems to stop you from doing anything nice using IntoIterator or Extend. 😞
Description
Following #3575 (which was inspired by me complaining on your discord that this was missing from the interface) and the discussion on your discord, I figured I would fancy a slightly different approach to that PR and the interfaces introduced in your PR there.
This is twofold:
RelayMap::extendwith an actualimpl Extend ror RelayMap(that can easily be used with an existingRelayMapby callingrm.relays::<Vec<_>>()on itRelayMap::extendwith anRelayMap::join. Reason here is that my usecase (that, again, inspired this to be added) is to build aRelayMapobject with defaults + custom entries before using them, so there's no need to update (.extend()ing) an existingRelayMapduring runtime. This is still preserved via theimpl Extendthough.Tell me what you think! CCing people from discord:
Breaking Changes
RelayMap::extend(unreleased) ->RelayMap::join