You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Due to Duration.Inf being a specific instance, Promise.tryAwait(...) fails with a MatchError if Duration.Inf is serialized and deserialized (e.g. using Akka remotely)
#9197
scala.concurrent.duration.Duration.Inf is a specific instance - i.e.
valInf:Infinite=newInfinite {
overridedeftoString="Duration.Inf"defcompare(other: Duration) = other match {
case x if x eq Undefined=>-1// Undefined != Undefinedcase x if x eq this=>0// `case Inf` will include null checks in the byte codecase _ =>1
}
defunary_-:Duration=MinusInfdeftoUnit(unit: TimeUnit):Double=Double.PositiveInfinity
}
This all works brilliantly unless you send it from one VM to another (say via a remote Akka call). If you do, it creates a new "Duration.Inf" instance, so now it is no longer a singleton in the receiving VM.
If you then pass this new Duration.Inf into Await.result or something you receive a MatchError in scala.concurrent.impl.Promise.tryAwait(...) here:
/** Try waiting for this promise to be completed.*/protectedfinaldeftryAwait(atMost: Duration):Boolean=if (!isCompleted) {
importDuration.Undefinedimportscala.concurrent.Future.InternalCallbackExecutor
atMost match {
case e if e eq Undefined=>thrownewIllegalArgumentException("cannot wait for Undefined period")
caseDuration.Inf=>vall=newCompletionLatch[T]()
onComplete(l)(InternalCallbackExecutor)
l.acquireSharedInterruptibly(1)
caseDuration.MinusInf=>// Drop outcasef: FiniteDuration=>if (f >Duration.Zero) {
vall=newCompletionLatch[T]()
onComplete(l)(InternalCallbackExecutor)
l.tryAcquireSharedNanos(1, f.toNanos)
}
}
isCompleted
} elsetrue// Already completed
i.e. It attempts to match the Duration.Inf instances and fails because they are different - and you end up with a confusing error saying "MatchError: Duration.Inf" even though there is clearly a match case for it.
The text was updated successfully, but these errors were encountered:
@Ichoran said:
I think overriding readResolve will solve this one. Not technically binary compatible, I don't think, but it's going to be awfully tricky to do it otherwise. Only string identity is safe, and users might also employ the eq Inf pattern.
@Ichoran said:
Well, the fix fixes the problem, but it's outrageously incompatible because the autogenerated serialVersionUID changes when you add readResolve. So--boom!--you can't pass these around. Finding and fixing every "eq Inf" is messy and painful.
It's possible, I guess, to ask ASM to rewrite the bytecode of scala.concurrent.duration.Duration$$anon$1 through 3 to contain a private static final long serialVersionUID of the right value. But, ugh?
@Ichoran said:
Turns out readResolve can be private, so even though the consistency of serialVersionUID is still in question, it at least isn't changed by adding the readResolves that turn the serialized copy into just a reference to the local singleton.
scala.concurrent.duration.Duration.Inf is a specific instance - i.e.
This all works brilliantly unless you send it from one VM to another (say via a remote Akka call). If you do, it creates a new "Duration.Inf" instance, so now it is no longer a singleton in the receiving VM.
If you then pass this new Duration.Inf into Await.result or something you receive a MatchError in scala.concurrent.impl.Promise.tryAwait(...) here:
i.e. It attempts to match the Duration.Inf instances and fails because they are different - and you end up with a confusing error saying "MatchError: Duration.Inf" even though there is clearly a match case for it.
The text was updated successfully, but these errors were encountered: