Make StringQuoter date parsing thread-safe#10322
Conversation
RequestFactory and AutoBean decode Date values on the server through StringQuoter.tryParseDate, which shared two static SimpleDateFormat instances across all request threads. SimpleDateFormat parsing mutates internal Calendar state, so concurrent requests race and return wrong dates or throw. Give each thread its own formatter via ThreadLocal.
|
Interesting - the code is definitely wrong, but does this actually fail in any real use case? It looks like you could manufacture such a scenario by manually writing JSON, but clearly AutoBeans (and thus, RequestFactory) won't serialize dates using these formats. I imagine these were speculatively added, but never actually used. gwt/user/src/com/google/web/bindery/autobean/shared/ValueCodex.java Lines 113 to 115 in 4e6ae10 That seems contradicted by discussion at #6330 (esp #6330 (comment)) and 3df95e6. Nevertheless, there are still no tests using date strings that I can find. I'm apprehensive about merging this despite its correctness, given the abandoned #10307 and the behavior of the account (many PRs to many unrelated repos with weak followup and tendency towards LLM-like content). |
|
If the code was never used, then it could be removed, no? However, if it is in use, it looks more correct than before, proposed by a LLM or not, no? |
|
Right - the code looks right, but the use case needs tests. The linked issue makes me suspect it is (or at least "was") used in some capacity, so removing a feature of a public api is at least somewhat dangerous, and there's probably no downside... but especially if it is tool-generated code, it should come with a test. As a maintainer, I think it is appropriate for me to have a degree of skepticism for "more correct than before" code coming from low-effort sources, as a way to gain trust and attack the project. I'm definitely not accusing @metsw24-max here - the fixes to all the various repos they've sent code to seem well-intentioned, but very few have tests or actual use cases. |
|
Fair point on the missing tests. Pushed a JRE test (StringQuoterJreTest) that covers the millis, ISO-8601, Z-suffix, RFC 2822, and unparseable paths, plus a concurrent hammer on tryParseDate that fires 16 threads x 2000 parses at the same date string. On the unpatched code it reliably produces wrong Dates (got ~11 on my box); with the ThreadLocal it sits at zero. Also wired into AutoBeanSuite. |
|
Thanks - locally I haven't had the test pass without the fix, though I did see only one wrong date value (out of 16*2000 checks) - usually single digit, but rather less than 5, so the test does what it is there to do. |
|
Why wasn't this caught by https://errorprone.info/bugpattern/DateFormatConstant ? The rule exists in 2.33. |
|
@zbynek nice find - it looks like that is configured as a warning rather than an error, and common.ant.xml sets google/error-prone#424 exists to let projects pick out warnings to disable or let them fail the build, but hasn't been fixed. When I turn warnings on, I don't see the DateFormatConstant in the output - we might have too many warnings to see all of them... Here's a count of each warning type, via The four DateFormatConstant issues are this one class, repeated twice. To repro this list: diff --git a/common.ant.xml b/common.ant.xml
index d5e0312342..c10d87c16c 100755
--- a/common.ant.xml
+++ b/common.ant.xml
@@ -55,7 +55,7 @@
<property name="javac.debuglevel" value="lines,vars,source"/>
<property name="javac.encoding" value="utf-8"/>
<property name="javac.release" value="11"/>
- <property name="javac.nowarn" value="true"/>
+ <property name="javac.nowarn" value="false"/>
<!-- javac and errorprone instructions from https://errorprone.info/docs/installation#ant -->
<path id="errorprone.processorpath.ref">
@@ -184,6 +184,8 @@
<compilerarg value="-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" />
<compilerarg value="-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED" />
<compilerarg value="-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED" />
+ <compilerarg value="-Xmaxwarns" />
+ <compilerarg value="99999" />
<compilerarg value="-XDcompilePolicy=simple"/>
<compilerarg value="-processorpath"/>
<compilerarg pathref="mergedprocessorpath.ref"/> |
|
Bumping this one. The harness rework is in 9efec11 and covers jnehlmeier's four points along with zbynek's string-literal note. It still fails reliably without the ThreadLocal and stays clean with it, which lines up with what you saw locally. From the errorprone thread: this change also clears the DateFormatConstant warnings for this class, since those are flagging the two shared SimpleDateFormat constants the PR replaces. Happy to make any further tweaks needed to land it. |
jnehlmeier
left a comment
There was a problem hiding this comment.
Looks good. After the copyright change I am fine with it.
| @@ -0,0 +1,118 @@ | |||
| /* | |||
| * Copyright 2026 Google Inc. | |||
There was a problem hiding this comment.
Given it is a new file it should be Copyright 2026 GWT Project Authors.
There was a problem hiding this comment.
Done, header now reads Copyright 2026 GWT Project Authors. Pushed in dbf5570.
Used ErrorProne's patch functionality to add override annotations where possible. Used regular expressions to make sure overrides are on a separate line. This should make it easier to show errorprone warnings during compilation without going through much spam -- this is the most common errorprone as pointed out in #10322 (comment).
RequestFactory and AutoBean decode Date values on the server through StringQuoter.tryParseDate, which shared two static SimpleDateFormat instances across all request threads. SimpleDateFormat parsing mutates internal Calendar state, so concurrent requests race and return wrong dates or throw. Give each thread its own formatter via ThreadLocal.