-
Bug
-
Resolution: Unresolved
-
P4
-
11
The TSAN checker (https://wiki.openjdk.org/display/tsan) reports a data race on jdk.internal.net.http.Http1Exchange#state.
The stack traces below are based on JDK 11:
WARNING: ThreadSanitizer: data race (pid=1838)
Read of size 4 at 0x00008ad94068 by thread T114 (mutexes: write M0):
#0 jdk.internal.net.http.Http1Exchange$Http1Publisher$WriteTask.run()V Http1Exchange.java:648
#1 jdk.internal.net.http.common.SequentialScheduler$SynchronizedRestartableTask.run()V SequentialScheduler.java:175
#2 jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(Ljdk/internal/net/http/common/SequentialScheduler$DeferredCompleter;)V SequentialScheduler.java:147
#3 jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run()V SequentialScheduler.java:198
#4 jdk.internal.net.http.HttpClientImpl$DelegatingExecutor.execute(Ljava/lang/Runnable;)V HttpClientImpl.java:153
#5 jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(Ljdk/internal/net/http/common/SequentialScheduler$SchedulableTask;Ljava/util/concurrent/Executor;)V SequentialScheduler.java:273
#6 jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(Ljava/util/concurrent/Executor;)V SequentialScheduler.java:242
#7 jdk.internal.net.http.Http1Exchange$Http1Publisher$Http1WriteSubscription.request(J)V Http1Exchange.java:704
#8 jdk.internal.net.http.SocketTube$InternalWriteSubscriber$WriteSubscription.requestMore()V SocketTube.java:503
#9 jdk.internal.net.http.SocketTube$InternalWriteSubscriber.requestMore()V SocketTube.java:388
#10 jdk.internal.net.http.SocketTube$InternalWriteSubscriber$$Lambda$452.run()V ??
#11 java.util.concurrent.ThreadPoolExecutor.runWorker(Ljava/util/concurrent/ThreadPoolExecutor$Worker;)V ThreadPoolExecutor.java:1130
#12 java.util.concurrent.ThreadPoolExecutor$Worker.run()V ThreadPoolExecutor.java:630
#13 java.lang.Thread.run()V Thread.java:830
#14 jdk.internal.misc.InnocuousThread.run()V InnocuousThread.java:161
#15 (Generated Stub) <null>
Previous write of size 4 at 0x00008ad94068 by thread T111 (mutexes: write M1):
#0 jdk.internal.net.http.Http1Exchange.lambda$sendHeadersAsync$4(Ljava/lang/Void;)Ljava/util/concurrent/CompletionStage; Http1Exchange.java:276
#1 jdk.internal.net.http.Http1Exchange$$Lambda$437.apply(Ljava/lang/Object;)Ljava/lang/Object; ??
#2 java.util.concurrent.CompletableFuture$UniCompose.tryFire(I)Ljava/util/concurrent/CompletableFuture; CompletableFuture.java:1072
#3 java.util.concurrent.CompletableFuture.postComplete()V CompletableFuture.java:506
#4 java.util.concurrent.CompletableFuture$AsyncSupply.run()V CompletableFuture.java:1705
#5 java.util.concurrent.ThreadPoolExecutor.runWorker(Ljava/util/concurrent/ThreadPoolExecutor$Worker;)V ThreadPoolExecutor.java:1130
#6 java.util.concurrent.ThreadPoolExecutor$Worker.run()V ThreadPoolExecutor.java:630
#7 java.lang.Thread.run()V Thread.java:830
#8 jdk.internal.misc.InnocuousThread.run()V InnocuousThread.java:161
#9 (Generated Stub) <null>
I think the race is benign. It could be worth considering fixing the race to make the code more self evidently correct. Alternately, if the race is confirmed to be benign that could be worth documenting in source (similar to e.g.JDK-8309688).
Looking at the code, WriteTask asserts `state != COMPLETED`, and the only place that sets `state` to `COMPLETED` is the same method in WriteTask that does the assertion. WriteTask is exclusively used with SequentialScheduler.lockingScheduler(...), which guarantees the task is executed sequentially and does external synchronization. So I think that's fine: the task does a racy read, but the only values it will ever read racily will not cause the assertion to fail.
The stack traces below are based on JDK 11:
WARNING: ThreadSanitizer: data race (pid=1838)
Read of size 4 at 0x00008ad94068 by thread T114 (mutexes: write M0):
#0 jdk.internal.net.http.Http1Exchange$Http1Publisher$WriteTask.run()V Http1Exchange.java:648
#1 jdk.internal.net.http.common.SequentialScheduler$SynchronizedRestartableTask.run()V SequentialScheduler.java:175
#2 jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(Ljdk/internal/net/http/common/SequentialScheduler$DeferredCompleter;)V SequentialScheduler.java:147
#3 jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run()V SequentialScheduler.java:198
#4 jdk.internal.net.http.HttpClientImpl$DelegatingExecutor.execute(Ljava/lang/Runnable;)V HttpClientImpl.java:153
#5 jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(Ljdk/internal/net/http/common/SequentialScheduler$SchedulableTask;Ljava/util/concurrent/Executor;)V SequentialScheduler.java:273
#6 jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(Ljava/util/concurrent/Executor;)V SequentialScheduler.java:242
#7 jdk.internal.net.http.Http1Exchange$Http1Publisher$Http1WriteSubscription.request(J)V Http1Exchange.java:704
#8 jdk.internal.net.http.SocketTube$InternalWriteSubscriber$WriteSubscription.requestMore()V SocketTube.java:503
#9 jdk.internal.net.http.SocketTube$InternalWriteSubscriber.requestMore()V SocketTube.java:388
#10 jdk.internal.net.http.SocketTube$InternalWriteSubscriber$$Lambda$452.run()V ??
#11 java.util.concurrent.ThreadPoolExecutor.runWorker(Ljava/util/concurrent/ThreadPoolExecutor$Worker;)V ThreadPoolExecutor.java:1130
#12 java.util.concurrent.ThreadPoolExecutor$Worker.run()V ThreadPoolExecutor.java:630
#13 java.lang.Thread.run()V Thread.java:830
#14 jdk.internal.misc.InnocuousThread.run()V InnocuousThread.java:161
#15 (Generated Stub) <null>
Previous write of size 4 at 0x00008ad94068 by thread T111 (mutexes: write M1):
#0 jdk.internal.net.http.Http1Exchange.lambda$sendHeadersAsync$4(Ljava/lang/Void;)Ljava/util/concurrent/CompletionStage; Http1Exchange.java:276
#1 jdk.internal.net.http.Http1Exchange$$Lambda$437.apply(Ljava/lang/Object;)Ljava/lang/Object; ??
#2 java.util.concurrent.CompletableFuture$UniCompose.tryFire(I)Ljava/util/concurrent/CompletableFuture; CompletableFuture.java:1072
#3 java.util.concurrent.CompletableFuture.postComplete()V CompletableFuture.java:506
#4 java.util.concurrent.CompletableFuture$AsyncSupply.run()V CompletableFuture.java:1705
#5 java.util.concurrent.ThreadPoolExecutor.runWorker(Ljava/util/concurrent/ThreadPoolExecutor$Worker;)V ThreadPoolExecutor.java:1130
#6 java.util.concurrent.ThreadPoolExecutor$Worker.run()V ThreadPoolExecutor.java:630
#7 java.lang.Thread.run()V Thread.java:830
#8 jdk.internal.misc.InnocuousThread.run()V InnocuousThread.java:161
#9 (Generated Stub) <null>
I think the race is benign. It could be worth considering fixing the race to make the code more self evidently correct. Alternately, if the race is confirmed to be benign that could be worth documenting in source (similar to e.g.
Looking at the code, WriteTask asserts `state != COMPLETED`, and the only place that sets `state` to `COMPLETED` is the same method in WriteTask that does the assertion. WriteTask is exclusively used with SequentialScheduler.lockingScheduler(...), which guarantees the task is executed sequentially and does external synchronization. So I think that's fine: the task does a racy read, but the only values it will ever read racily will not cause the assertion to fail.