EWS doesn't hate it anymore!

Reviews welcome.  I've been slowly integrating feedback as I've received it.

-Filip



> On Nov 4, 2016, at 11:52 AM, Filip Pizlo <[email protected]> wrote:
> 
> Haha, I'm fixing it!
> 
> I could use a review of the time API even while I fix some broken corners in 
> WebCore and WK2.
> 
> -Filip
> 
> 
>> On Nov 4, 2016, at 11:31 AM, Brent Fulgham <[email protected] 
>> <mailto:[email protected]>> wrote:
>> 
>> EWS Hates your patch! :-)
>> 
>>> On Nov 4, 2016, at 10:01 AM, Filip Pizlo <[email protected] 
>>> <mailto:[email protected]>> wrote:
>>> 
>>> Hi everyone!
>>> 
>>> That last time we talked about this, there seemed to be a lot of agreement 
>>> that we should go with the Seconds/MonotonicTime/WallTime approach.
>>> 
>>> I have implemented it: https://bugs.webkit.org/show_bug.cgi?id=152045 
>>> <https://bugs.webkit.org/show_bug.cgi?id=152045>
>>> 
>>> That patch just takes a subset of our time code - all of the stuff that 
>>> transitively touches ParkingLot - and converts it to use the new time 
>>> classes.  Reviews welcome!
>>> 
>>> -Filip
>>> 
>>> 
>>> 
>>>> On May 22, 2016, at 6:41 PM, Filip Pizlo <[email protected] 
>>>> <mailto:[email protected]>> wrote:
>>>> 
>>>> Hi everyone!
>>>> 
>>>> I’d like us to stop using std::chrono and go back to using doubles for 
>>>> time.  First I list the things that I think we wanted to get from 
>>>> std::chrono - the reasons why we started switching to it in the first 
>>>> place.  Then I list some disadvantages of std::chrono that we've seen from 
>>>> fixing std::chrono-based code.  Finally I propose some options for how to 
>>>> use doubles for time.
>>>> 
>>>> Why we switched to std::chrono
>>>> 
>>>> A year ago we started using std::chrono for measuring time.  std::chrono 
>>>> has a rich typesystem for expressing many different kinds of time.  For 
>>>> example, you can distinguish between an absolute point in time and a 
>>>> relative time.  And you can distinguish between different units, like 
>>>> nanoseconds, milliseconds, etc.
>>>> 
>>>> Before this, we used doubles for time.  std::chrono’s advantages over 
>>>> doubles are:
>>>> 
>>>> Easy to remember what unit is used: We sometimes used doubles for 
>>>> milliseconds and sometimes for seconds.  std::chrono prevents you from 
>>>> getting the two confused.
>>>> 
>>>> Easy to remember what kind of clock is used: We sometimes use the 
>>>> monotonic clock and sometimes the wall clock (aka the real time clock).  
>>>> Bad things would happen if we passed a time measured using the monotonic 
>>>> clock to functions that expected time measured using the wall clock, and 
>>>> vice-versa.  I know that I’ve made this mistake in the past, and it can be 
>>>> painful to debug.
>>>> 
>>>> In short, std::chrono uses compile-time type checking to catch some bugs.
>>>> 
>>>> Disadvantages of using std::chrono
>>>> 
>>>> We’ve seen some problems with std::chrono, and I think that the problems 
>>>> outweigh the advantages.  std::chrono suffers from a heavily templatized 
>>>> API that results in template creep in our own internal APIs.  
>>>> std::chrono’s default of integers without overflow protection means that 
>>>> math involving std::chrono is inherently more dangerous than math 
>>>> involving double.  This is particularly bad when we use time to speak 
>>>> about timeouts.
>>>> 
>>>> Too many templates: std::chrono uses templates heavily.  It’s overkill for 
>>>> measuring time.  This leads to verbosity and template creep throughout 
>>>> common algorithms that take time as an argument.  For example if we use 
>>>> doubles, a method for sleeping for a second might look like 
>>>> sleepForSeconds(double).  This works even if someone wants to sleep for a 
>>>> nanoseconds, since 0.000001 is easy to represent using a double.  Also, 
>>>> multiplying or dividing a double by a small constant factor (1,000,000,000 
>>>> is small by double standards) is virtually guaranteed to avoid any loss of 
>>>> precision.  But as soon as such a utility gets std::chronified, it becomes 
>>>> a template.  This is because you cannot have 
>>>> sleepFor(std::chrono::seconds), since that wouldn’t allow you to represent 
>>>> fractions of seconds.  This brings me to my next point.
>>>> 
>>>> Overflow danger: std::chrono is based on integers and its math methods do 
>>>> not support overflow protection.  This has led to serious bugs like 
>>>> https://bugs.webkit.org/show_bug.cgi?id=157924 
>>>> <https://bugs.webkit.org/show_bug.cgi?id=157924>.  This cancels out the 
>>>> “remember what unit is used” benefit cited above.  It’s true that I know 
>>>> what type of time I have, but as soon as I duration_cast it to another 
>>>> unit, I may overflow.  The type system does not help!  This is insane: 
>>>> std::chrono requires you to do more work when writing multi-unit code, so 
>>>> that you satisfy the type checker, but you still have to be just as 
>>>> paranoid around multi-unit scenarios.  Forgetting that you have 
>>>> milliseconds and using it as seconds is trivially fixable.  But if 
>>>> std::chrono flags such an error and you fix it with a duration_cast (as 
>>>> any std::chrono tutorial will tell you to do), you’ve just introduced an 
>>>> unchecked overflow and such unchecked overflows are known to cause bugs 
>>>> that manifest as pages not working correctly.
>>>> 
>>>> I think that doubles are better than std::chrono in multi-unit scenarios.  
>>>> It may be possible to have std::chrono work with doubles, but this 
>>>> probably implies us writing our own clocks.  std::chrono’s default clocks 
>>>> use integers, not doubles.  It also may be possible to teach std::chrono 
>>>> to do overflow protection, but that would make me so sad since using 
>>>> double means not having to worry about overflow at all.
>>>> 
>>>> The overflow issue is interesting because of its implications for how we 
>>>> do timeouts.  The way to have a method with an optional timeout is to do 
>>>> one of these things:
>>>> 
>>>> - Use 0 to mean no timeout.
>>>> - Have one function for timeout and one for no timeout.
>>>> - Have some form of +Inf or INT_MAX to mean no timeout.  This makes so 
>>>> much mathematical sense.
>>>> 
>>>> WebKit takes the +Inf/INT_MAX approach.  I like this approach the best 
>>>> because it makes the most mathematical sense: not giving a timeout is 
>>>> exactly like asking for a timeout at time-like infinity.  When used with 
>>>> doubles, this Just Works.  +Inf is greater than any value and it gets 
>>>> preserved properly in math (+Inf * real = +Inf, so it survives gracefully 
>>>> in unit conversions; +Inf + real = +Inf, so it also survives 
>>>> absolute-to-relative conversions).
>>>> 
>>>> But this doesn’t work with std::chrono.  The closest thing to +Inf is 
>>>> duration::max(), i.e. some kind of UINT_MAX, but this is guaranteed to 
>>>> overflow anytime it’s converted to a more precise unit of time 
>>>> (nanoseconds::max() converted to milliseconds is something bogus).  It 
>>>> appears that std::chrono doesn’t have a good story for infinite timeout, 
>>>> which means that anyone writing a function that can optionally have a 
>>>> timeout is going to have a bad time.  We have plenty of such functions in 
>>>> WebKit.  For example, I’m not sure how to come up with a feel-good 
>>>> solution to https://bugs.webkit.org/show_bug.cgi?id=157937 
>>>> <https://bugs.webkit.org/show_bug.cgi?id=157937> so long as we use 
>>>> std::chrono.
>>>> 
>>>> Going back to doubles
>>>> 
>>>> Considering these facts, I propose that we switch back to using doubles 
>>>> for time.  We can either simply revert to the way we used doubles before, 
>>>> or we can come up with some more sophisticated approach that blends the 
>>>> best of both worlds.  I prefer plain doubles because I love simplicity.
>>>> 
>>>> Simply revert to our old ways: I like this approach the best because it 
>>>> involves only very simple changes.  Prior to std::chrono, we used a double 
>>>> to measure time in seconds.  It was understood that seconds was the 
>>>> default unit.  We would use both monotonic and wall clocks, and we used 
>>>> double for both of them.
>>>> 
>>>> Come up with a new type system: Having learned from std::chrono and 
>>>> doubles, it seems that the best typesystem for time would comprise three 
>>>> classes: Seconds, WallTime, and MonotonicTime.  Seconds would be a class 
>>>> that holds a double and supports +/+=/-/-=/</<=/>/>=/==/!= operations, as 
>>>> well as conversions to a raw double for when you really need it.  WallTime 
>>>> and MonotonicTime would be wrappers for Seconds with a more limited set of 
>>>> available operations.  You can convert WallTime or MonotonicTime to 
>>>> Seconds and vice-versa, but some operators are overloaded to make casts 
>>>> unnecessary in most cases (WallTime + Seconds = WallTime, WallTime - 
>>>> WallTime = Seconds, etc).  This would save us from forgetting the unit or 
>>>> the clock.  The name of the Seconds class is a dead give-away, and 
>>>> WallTime and MonotonicTime will not yield you a value that is 
>>>> unit-sensitive unless you say something like WallTime::toSeconds().  There 
>>>> will be no easy way to convert WallTime to MonotonicTime and vice-versa, 
>>>> since we want to discourage such conversions.
>>>> 
>>>> Personally I feel very comfortable with doubles for time.  I like to put 
>>>> the word “Seconds” into variable names and function names 
>>>> (waitForSeconds(double) is a dead give-away).  On the other hand, I sort 
>>>> of like the idea of a type system to protect clock mix-ups.  I think 
>>>> that’s the biggest benefit we got from std::chrono.
>>>> 
>>>> If it was entirely up to me, I’d go for doubles.  I think that there needs 
>>>> to be a high burden of proof for using types to catch semantic bugs.  A 
>>>> type system *will* slow you down when writing code, so the EV (expected 
>>>> value) of the time savings from bugs caught early needs to be greater than 
>>>> the EV of the time lost due to spoonfeeding the compiler or having to 
>>>> remember how to use those classes.  Although I know that using doubles 
>>>> sometimes meant we had bugs, I don’t think they were frequent or severe 
>>>> enough for the odds to be good for the Seconds/WallTime/MonotonicTime 
>>>> solution.
>>>> 
>>>> Thoughts?
>>>> 
>>>> -Filip
>>>> 
>>> 
>>> _______________________________________________
>>> webkit-dev mailing list
>>> [email protected] <mailto:[email protected]>
>>> https://lists.webkit.org/mailman/listinfo/webkit-dev 
>>> <https://lists.webkit.org/mailman/listinfo/webkit-dev>
>> 
> 
> _______________________________________________
> webkit-dev mailing list
> [email protected]
> https://lists.webkit.org/mailman/listinfo/webkit-dev

_______________________________________________
webkit-dev mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-dev

Reply via email to