Skip to content

Time.kt uses Duration.toMillis in unsafe manner #868

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

Closed
Groostav opened this issue Nov 30, 2018 · 9 comments
Closed

Time.kt uses Duration.toMillis in unsafe manner #868

Groostav opened this issue Nov 30, 2018 · 9 comments

Comments

@Groostav
Copy link
Contributor

Using Duration.ofSeconds(Long.MAX_VALUE) as the timeout argument for kotlinx.coroutines.withTimeout will cause an ArithmaticOverflowException

This is because, in pure java Durations.ofSeconds(Long.MAX_VALUE).toMillis() will cause an Arithmatic overflow.

I've got a fix that basically involves adding an extension function toMillisOrMax that returns Long.MAX_VALUE when toMillis would otherwise overflow.

Use case:

I'm a big fan of null objects, and the way I've structured things is such that the "default" timeout is Duration.of(seconds = Long.MAX_VALUE, nanos = 999__999_999) (which is effectively Duration.MAX_VALUE), such that our down-stack code need not consider the case where no timeout is specified.

If you guys are interested in this fix I'll submit a pull request to that involves simply:

private fun Duration.toMillisOrMax(): Long = when(this.seconds) {
    in 0L .. (Long.MAX_VALUE / MILLIS_PER_SECOND) -> duration.toMillis()
    else -> Long.MAX_VALUE
}

as a function in TimeKt, invoked instead of Duration.toMillis()

@fvasco
Copy link
Contributor

fvasco commented Nov 30, 2018

I simple workaround is to avoid time check at all if the duration exceed a large number.

So if the duration is greater than a century (example) then the code is executed without any timeout (this avoid some system calls and related work).

@qwwdfsad
Copy link
Member

qwwdfsad commented Dec 13, 2018

I simple workaround is to avoid time check at all if the duration exceeds a large number.
So if the duration is greater than a century (example) then the code is executed without any timeout (this avoid some system calls and related work).

The library should not do that. There are a lot of valid use-cases such as calling delay(MAX_VALUE) to rely on cancellation or withTimeout(MAX_VALUE) to rely on virtual time or something else (e.g. interference with child jobs and nested coroutines). Such behaviour ("do nothing if the timeout is greater than a century") is very cumbersome and unobvious and may interfere with thigs like NTP in a very obscure ways.

Not sure that Duration.toMillisOrMax is a good solution either, as it obviously break contract "throws TimeoutException if a specified timeout was exceeded"

fvasco added a commit to fvasco/kotlinx.coroutines that referenced this issue Feb 23, 2019
fvasco added a commit to fvasco/kotlinx.coroutines that referenced this issue Feb 23, 2019
fvasco added a commit to fvasco/kotlinx.coroutines that referenced this issue Feb 23, 2019
@fvasco
Copy link
Contributor

fvasco commented Feb 25, 2019

@qwwdfsad Do you consider coerce at most 292 million years each delay/timeout a good solution?
How this integration should handle the FOREVER Duration?

fvasco added a commit to fvasco/kotlinx.coroutines that referenced this issue Feb 25, 2019
fvasco added a commit to fvasco/kotlinx.coroutines that referenced this issue Feb 26, 2019
fvasco added a commit to fvasco/kotlinx.coroutines that referenced this issue Feb 27, 2019
@avently
Copy link

avently commented Mar 23, 2019

Why not just use Long instead of this custom Duration? It breaks from migration to common code in the future.

@fvasco
Copy link
Contributor

fvasco commented Mar 23, 2019

Long is a pure number, it miss of unit of measurement.

@avently
Copy link

avently commented Mar 23, 2019

Yes, but I didn't meet developer who doesn't know how to make minutes, hours, etc with Long.
So Duration class isn't a standart way to do things and it gives more sugar to your lib's code (converting from long to duration and back). Also it breaks some things in multiplatform projects. I forgot actual example, but I got someday classnotfoundexception because of duration

@JakeWharton
Copy link
Contributor

Duration is actually the standard and long is the legacy mechanism.

@fvasco
Copy link
Contributor

fvasco commented Mar 23, 2019

kotlinx-coroutines-jdk8 isn't a multiplatform project.

@avently
Copy link

avently commented Mar 24, 2019

Obviously. But I tried to migrate some java only code to common from ktor websockets (it has only java part now). One of the blockers was java.time package. That's why I think about migration from platform-specific to universal code in every project

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants