-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Duration Reform RFC #1040
Duration Reform RFC #1040
Conversation
|
||
`Duration` implements: | ||
|
||
* `Add`, `Sub`, `Mul`, `Div` which follow the overflow and underflow |
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 assume the Sub
impl will panic if the result would be negative?
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, that is the intention.
cc @lifthrasiir |
```rust | ||
pub struct Duration { | ||
secs: u64, | ||
nanos: u32 // may not be more than 1 billion |
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.
Does this mean it is allowed to be exactly 1 billion, or is only 999,999,999 allowed?
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.
Based on
Nanoseconds […] carry into the secs field.
later in the proposal, the latter. But yeah, should be fixed.
👍 |
/// create a Duration from a number of milliseconds | ||
pub fn from_millis(millis: u64) -> Timeout; | ||
|
||
/// the number of seconds represented by the Timeout |
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.
s/Timeout/Duration/
This is a clean and lean proposal that optimizes for the most important use cases, thereby sidestepping a lot of thorniness with the current type. 👍 from me. |
can never exceed 1 billion or be less than 0, and carry into the | ||
`secs` field. | ||
* `Display`, which prints a number of seconds, milliseconds and | ||
nanoseconds (if more than 0). |
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.
seconds, milliseconds and nanoseconds is the same unit with diffrent multipliers. Does this propose to display the same thing in three dirferent ways, e.g. Duration(15.0010000001s, 15001.000001ms, 15001000001ns)
?
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 it's saying that durations would be printed like 15 seconds, 306 milliseconds, and 13 nanoseconds
.
Overall 👍 |
|
||
* `Add`, `Sub`, `Mul`, `Div` which follow the overflow and underflow | ||
rules for `u64` when applied to the `secs` field. Nanoseconds | ||
can never exceed 1 billion or be less than 0, and carry into the |
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.
less than 0 is implied by the type, and as above this should never exceed 999,999,999.
As the original author of I still argue that the resulting type should not be called |
Scala has Internally a duration is either an infinite duration or a finite duration which is represented as a pair
val d1 = Duration(5, TimeUnit.SECONDS);
val d2 = Duration(1, TimeUnit.HOURS); There is also a convenient DSL implemented via implicits: val d1 = 5.seconds
val d2 = 1.hour Arithmetic operations on durations may "downgrade" the time unit, so the duration is always represented accurately. Because of such uniformity the max duration which may be represented by |
@lifthrasiir I can see where you're coming from re: the Chrono @netvl One interesting thing to consider (in the future extensibility sense) is that a 32-bit nanoseconds value has significant headroom - it may be possible to, in the future, either use the high bits as flag bits for 'infinite' &c, or use higher values as sentinels for special behaviors (including 'infinite'). One thing that immediately springs to mind: the high bit of I'm not suggesting adding this to the RFC, but it's something to keep in mind IMO. @wycats However, as a result of the above, I do think EDIT: To expand on the "flag bits in syscalls" thing, several Linux syscalls that took a "flags" parameter only checked for the defined flags when the syscall was introduced. As a result, programs passing garbage bits in undefined positions passed unnoticed, creating a back-compat hazard: Any future use of those bits would cause behavior changes in any program that passed garbage data, which used to silently succeed. These days, any new syscall is expected to error out if any undefined flag bits are nonzero, preventing the back-compat hazard from arising. |
This is good, I think it's best to keep the Duration type as simple as possible, even in the future: instead of adding 'special values' to the Duration type to represent infinite durations or different units, I think a better route would be to wrap the Duration in an enum, which has cases Finite(Duration) and Infinite, and instead of different units, just add sufficiently many bits of precision that it can always use the highest precision unit. This avoids the need to branch at all when adding and manipulating durations, it makes it simpler and easier to understand, and it doesn't cost anything as Durations are 128 bits in size, but only use 96 bits, so the compiler can easily use the remaining 32 bits for determinant values. There are also places where an infinite duration doesn't make sense. |
If we had
|
From a back-seat perspective, @Diggsey 's comment that using an enum to wrap a duration for APIs that need it makes sense to me. |
@Diggsey I agree it should (API-level) present as an enum; I'm more talking about how the repr of that enum might be able to use the headroom in |
I updated this RFC, taking into consideration the comments here. |
Thanks @wycats! I am personally quite happy with the simple, clean, and conservative approach here. |
I personally don't like that the proposal constantly refers to date/time libraries and joda-time in particular. The standard And I also don't like that there is no way to work with milliseconds. Given the internal representation it absolutely makes sense to return an |
@netvl I disagree - In particular, I feel it's short-sighted to presume that the results of date calculations will not need to be used for durations and timeouts. A trivial example would be implementing Using the joda-time definition gives a path forward - for now, a Duration type that while not sufficient for date calculations, has a definition compatible with a robust, tested API that is - meaning that when such an API arrives, interoperation "falls into the correctness pit." @wycats Although, the RFC still seems to use |
But this does not affect the built-in duration implementation at all. If you work with real world date and time (including such things as cron), you will need to use proper date/time library anyway, and such library will always have means to convert its duration representation to the one from the standard library (or at least it would be able to provide milliseconds/nanoseconds value which could be converted to the standard library duration). The problem is, real world date/time manipulation is very complex, but most of this complexity is completely unnecessary for the main area where the built-in duration is used - that is, for threading and networking. We won't be able to combine real-world date/time handling and threading/networking duration anyway, and taking date/time libraries as a reference will only result in a half-baked API which would be unnecessarily generic and painful to use. |
@netvl, A good timeout format needs the following criteria:
The joda-time And the The |
@eternaleye, I'm not sure what you're getting at. While joda-time Duration representation indeed is suitable for networking/threading purposes (as it is just a number of milliseconds; I'd argue that nanosecond precision would be nicer, but it doesn't affect the general idea), I meant that I'm against linking the built-in duration to any real-world date/time library at all because their use cases are very different. When we have a proper officially supported date/time library, we will always be able to convert from whatever Anyway, units larger than second are used rarely, so I don't think the ability to create a "2 weeks" duration is that important. What is important is to have an ability to work with duration in terms of second fractions of various sizes efficiently - in particular, working with milliseconds. And that's why I think that we should take Java/Scala approach and represent the duration as a |
I'm not disagreeing over using Unix userspace historically used the current layout but with microseconds, and has moved to the layout proposed. Linux kernelspace is moving to 64-bit nanoseconds, though I can't recall the signedness (I think they are signed). Windows uses milliseconds or 'ticks', and I don't know the signedness. All have advantages and disadvantages. My disagreement is over your dislike of the references to joda-time. I feel they are valuable in that they are:
|
But this proposal just can't inhibit good date/time API because they are/will be completely disjoint and so it is unnecessary to regard this duration type in the context of a date/time library at all! That's what my point is about. Anyway, this discussion is getting increasingly more philosophical and disconnected from the actual proposal. I guess we should finish here. |
rules for `u64` when applied to the `secs` field (in particular, | ||
`Sub` will panic if the result would be negative). Nanoseconds | ||
must be less than 1 billion and great than or equal to 0, and carry | ||
into the `secs` field. |
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.
Will it follow the rules of u64
or will it also panic in non-debug mode?
I'd vote for the latter, because these duration-manipulating methods are unlikely to be called in performance-sensitive code.
I like the proposal, thanks for writing it up! |
I'm curious (and I don't think it's explained in the RFC), why does the representation mimic |
There's been a lot of positive feedback on this RFC, including from the original As always, remember that this represents preliminary approval of the design. The reformed module will be placed under its own feature gate, and we will solicit feedback, and iterate based on that experience, before fully committing to the new API. Thanks @wycats! |
Somewhat disappointing that no one commented on why seconds+nanoseconds is superior to just nanoseconds. It's not even in alternatives. |
@netvl |
@nagisa, why u64 is not big enough? It gives over 500 years of duration. When you would need more, given that |
And timespec compatibility is not very strong argument. It is not used by all API. I imagine that it is very possible to have e.g. Rust binding to some C library which accepts timeout in milliseconds. Windows API is also full of such functions, AFAIK. |
@netvl Sorry that I missed your question! My original design (when the type was called While I didn't try to spec out exactly what a broader-scope Can you say what benefits a |
Note that this is an implementation detail and can be changed anytime later without an RFC anyway back-compatibly. Re u64: It cannot represent all the values of timespec, hence restrincting use (useless or not) of system APIs to some extent. |
@nagisa I wouldn't considering reducing the available representable time to be an allowed backwards-compatible change, but it sounds like we're on the same page 😄 |
Isn't representing durations of hundreds of years at odds with the goal of avoiding "human complications"? Is e.g. |
I wouldn't call it a purely implementation detail since it can have a considerable effect on the API (I mean conversion operations).
I'm not sure what you mean. I only said that nanoseconds-based duration could support durations up to 2^64 nanoseconds which happens to be equal roughly to 450 years. Of course, internally it would be just nanoseconds.
Possible drawbacks (compared to timespec-like representation) are:
I personally think that 3rd point is of utmost importance and together with 2 outweighs drawback by a very large margin. Again, such approach is simpler - I think this is also important. Java/Scala live with flat duration for a very long time very nicely and I don't know any other language/stack which is more suited to large-scale concurrent and network programming - the areas where this duration type is going to be used. |
But why? This duration type can't possibly be used for date/time library since it would need completely different and more complex approach, and for threading/networking purposes 500 years are more than enough. |
I don't agree - I mean, the references to Joda-time make it pretty clear that this has essentially identical semantics to its Duration type. Largely because it did the sensible thing of separating physical delta time (Duration) from semantic interval time (Interval) from semantic point time (Instant). |
@eternaleye, Instant type does not make sense outside of date/time library and is not very useful for networking/threading purposes - and when we have a date/time library, it should provide necessary conversions anyway, for example, a duration until the given instance could be obtained like this: let d: Duration = (futureInstance - Instance::now()).into(); Durations in concurrency/networking and durations in real-world date/time are different things, and we should not conflate them, especially given that no standard date/time library exists now and is not yet planned (AFAIK). Durations which are discussed here should be as simple as possible. |
@netvl, I'm really not clear on what you're saying here. The "more complicated" type you're talking about seems to be identical to what Joda-time calls an Interval, not a Duration. What Joda-time calls a Duration is exactly such a simple type as you're describing: it is an integer count of uniform time units (seconds, nanoseconds, what-have-you) that avoids human issues like leap seconds because it counts time-that-passes, not time-between-points. And I wasn't in any way saying an Instant type should be used in network/threading, so I'm confused as to why you bring it up - I was just listing how Joda-time splits time-handling responsibilities among the three types. And the responsibilities it gives to Duration (that it's physical delta time, not semantic time of any kind) are the exact responsibilities that network/threading code wants in a timeout. |
@eternaleye You said precisely what I was going to respond. |
The only thing I wanted to say is that because usage areas of this duration type (network/concurrency) and date/time library duration type (real world date/time processing) are almost disjoint, and so it does not make sense to unify them, now or in future, even if their internal representation is similar or identical. Consequently it is not necessary for this duration type to have all features the real-world duration type, for example, to allow representation of durations larger than 500 years. |
@netvl, while your rationale is something I disagree with, I do think that making the Duration repr
|
@eternaleye, very nice summary, thanks! I didn't even think about alignment issues. |
A |
One thing I actually find myself wondering is whether That would require a bit more in the way of generic integer programming than is available now, though. |
Rendered