-
-
Notifications
You must be signed in to change notification settings - Fork 118
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
Move NumberFormat to thread-local variable #387
Conversation
The NumberFormat class isn't thread safe, and sharing one instance across threads allowed for races which resulted in otherwise valid OSM tags not being parsed correctly.
https://github.com/onthegomap/planetiler/actions/runs/3517874164 ℹ️ Base Logs c73c835
ℹ️ This Branch Logs 04ed8f7
|
From my reading of the issue, I believe this warning can be safely suppressed, but would like a second opinion. |
Yikes! Good catch. I'm a bit concerned about other locations that do this as well. It looks like both |
I think that's fine, can you put |
planetiler-core/src/main/java/com/onthegomap/planetiler/util/Format.java
Outdated
Show resolved
Hide resolved
Kudos, SonarCloud Quality Gate passed! |
pf = NumberFormat.getPercentInstance(locale); | ||
pf.setMaximumFractionDigits(0); | ||
nf = NumberFormat.getNumberInstance(locale); | ||
nf.setMaximumFractionDigits(1); | ||
intF = NumberFormat.getNumberInstance(locale); | ||
intF.setMaximumFractionDigits(0); | ||
pf = ThreadLocal.withInitial(() -> { | ||
var f = NumberFormat.getPercentInstance(locale); | ||
f.setMaximumFractionDigits(0); | ||
return f; | ||
}); | ||
nf = ThreadLocal.withInitial(() -> { | ||
var f = NumberFormat.getNumberInstance(locale); | ||
f.setMaximumFractionDigits(1); | ||
return f; | ||
}); | ||
intF = ThreadLocal.withInitial(() -> { | ||
var f = NumberFormat.getNumberInstance(locale); | ||
f.setMaximumFractionDigits(0); | ||
return f; | ||
}); |
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.
minor thing - you could probably just inline these in the field definitions since they don't need anything outside the initializer
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.
Is that possible if they need access to the locale
parameter passed to the constructor?
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.
oh yeah, good point!
The
NumberFormat
class isn't thread safe (StackOverflow, Docs), and sharing one instance across threads allowed for races which resulted in otherwise valid OSM tags not being parsed correctly.This surfaced as some rather strange exceptions during Planetiler runs that I wasn't able to reproduce when running each input individually. For example, both of these exceptions were not reproducible in unit tests:
Given that it's a race condition, I'm not sure how to write a proper test for this, but this change reliably fixes the issue for me during local runs, and the existing tests for
Parse
should at least confirm that there aren't regressions.