-
Notifications
You must be signed in to change notification settings - Fork 5
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
feat: extended counter updates and add signalling. #30
Conversation
- Added support for internal signaling between machines with Event::Signal. - It is now possible to update both counters on state transition. - A counter operation can now use the value of the other counter. - Updated changelog, also with minor clarifications.
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.
Nice! I left some comments about things that I think can be improved slightly. It's mostly nitpicks. But it would be more idiomatic, and potentially avoid panics (if future changes are not very careful)
In general it's a good rule of thumb to avoid unwrap
s as much as possible. It's almost always possible to access values in other, panic free, ways.
crates/maybenot/src/counter.rs
Outdated
match self.dist { | ||
None => 1, | ||
Some(dist) => { | ||
let s = dist.sample(rng) as u64; | ||
s.min(MAX_SAMPLED_COUNTER_VALUE) |
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 will not yield all values in the range 0 - MAX_SAMPLED_COUNTER_VALUE
. But I guess you know that. This part of the machine is not something I know very much about.
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 guess you're referring to the rounding down of the int part of f64
+ size difference? Ended up cutting the constant now, the converting from samples f64
to u64
is fine for all possible use-cases of the framework right now.
crates/maybenot/src/framework.rs
Outdated
self.transition(excluded, Event::Signal); | ||
} | ||
} | ||
self.signal_pending = None; |
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.
Instead of having to manually clear the signal, you can use Option::take
to get the value and at the same time set it to None
. if let Some(signal) = self.signal_pending.take() {...}
crates/maybenot/src/framework.rs
Outdated
} | ||
|
||
if let Some(excluded) = excluded { | ||
if self.signal_pending.unwrap().is_none() { |
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.
You already have access to the signal in the variable signal
here. So instead of this, would not just signal.is_none()
work? If you do use my Option::take
recommendation in the other comment, this would not work any longer, because the unwrap
would panic since the value is already taken out of the pending signal.
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 made the same comment to Ethan at first that we already have access to the signal, but it turns out that the signal is copied there (I think?), and when signal_pending
is set again in transition, we need to check for it again.
Regardless, I refactored this part, and it looks clearer now. Please let me know if you agree.
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.
That was indeed the issue - without accessing signal_pending directly, we didn't get the updated signal. The refactor also solves that problem, though, and I think it makes things look cleaner.
…hings a bit more clear
…d CounterZero events (possible thanks to supporting updating both counters at once)
I pushed another related fix here after a remark by Ethan: now that we can set two counters in the same operation, it was possible to create a machine with an infinite loop of Looking over the other events triggered internally for loops, I reason as follows:
Did I miss something? |
I'm happy with that solution for avoiding CounterZero loops. From my early experiences with the new v2 features, I think that machine developers will be able to live with it; in fact, it might not be an issue at all (triggering more than two Regarding the other events that might be able to loop, I reason in the same way - there seems to be no chance of looping if we consider them in isolation. The thing I try to be most careful about is creating the possibility of a loop when combining, e.g., signals and counters. I also don't think this is a problem, though, because we enforce the restrictions at a fairly high level in |
Thanks @ewitwer ! Then we're good from a machine developer's perspective feature-wise. Hmm, when combining events, it should be possible to do something like The most computationally intense action is That means an adversarially crafted machine can cause 30 samplings from distributions per event if no event batching occurs. When batching, we now limit the number of The outlier for batching becomes Edit: pushed how this would look like below. |
Singalling now happens at most once per machine and per trigger_events, instead of potentially once per machine and event. Improves safety for an integrator when batching due to load (by better reducing total computation) at the cost of machine developer's convenience.
Works for me. It is probably a better trade-off, at least for some integrations, to restrict signals on a per-batch basis like we do for counters. Semantically, I think it still makes sense sense to do it this way, even if it makes designing machines trickier. |
Great, that's a wrap for this PR. After this, will close and merge it, followed by some 2.0 work as planned. Just for future reference, on the topic of looping machines and concerns around adversarial machines: One blunt but effective solution is to add a |
@faern and @ewitwer , please have a look. I squashed the history.