Skip to content

Commit bf8672e

Browse files
committed
Fix data race in CircularOutputStream.
Calls to write(int) from different threads may read the same value of 'end'.
1 parent 9ec20e0 commit bf8672e

File tree

2 files changed

+51
-1
lines changed

2 files changed

+51
-1
lines changed

java/client/src/org/openqa/selenium/io/CircularOutputStream.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public CircularOutputStream() {
3939
}
4040

4141
@Override
42-
public void write(int b) throws IOException {
42+
public synchronized void write(int b) throws IOException {
4343
if (end == buffer.length) {
4444
filled = true;
4545
end = 0;

java/client/test/org/openqa/selenium/remote/internal/CircularOutputStreamTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import static org.junit.Assert.assertEquals;
2222

23+
import java.io.IOException;
2324
import org.junit.Test;
2425
import org.junit.runner.RunWith;
2526
import org.junit.runners.JUnit4;
@@ -127,4 +128,53 @@ public void testCircularness() {
127128
assertEquals("56789", os.toString());
128129
}
129130
}
131+
132+
@Test
133+
public void testConcurrentWrites() throws InterruptedException {
134+
final int bytesToWrite = 10000;
135+
CircularOutputStream os = new CircularOutputStream(2 * bytesToWrite);
136+
137+
Thread t1 = new Thread(new WriteChar(os, 'a', bytesToWrite));
138+
Thread t2 = new Thread(new WriteChar(os, 'b', bytesToWrite));
139+
t1.start();
140+
t2.start();
141+
t1.join();
142+
t2.join();
143+
144+
int a = 0;
145+
int b = 0;
146+
for (char c : os.toString().toCharArray()) {
147+
if (c == 'a') {
148+
a++;
149+
} else if (c == 'b') {
150+
b++;
151+
}
152+
}
153+
assertEquals(bytesToWrite, a);
154+
assertEquals(bytesToWrite, b);
155+
}
156+
157+
private static class WriteChar implements Runnable {
158+
private final CircularOutputStream stream;
159+
private final char c;
160+
private final int count;
161+
162+
public WriteChar(CircularOutputStream stream, char c, int count) {
163+
this.stream = stream;
164+
this.c = c;
165+
this.count = count;
166+
}
167+
168+
@Override
169+
public void run() {
170+
try {
171+
for (int i = 0; i < count; i++) {
172+
stream.write(c);
173+
Thread.yield();
174+
}
175+
} catch (IOException e) {
176+
// Ignore; the test will fail later when we discover that not all writes finished.
177+
}
178+
}
179+
}
130180
}

0 commit comments

Comments
 (0)