HADOOP-13500. Synchronizing iteration of Configuration properties object (#3775)
Signed-off-by: Akira Ajisaka <aajisaka@apache.org>
(cherry picked from commit 4483607a4e
)
This commit is contained in:
parent
e60b8a0358
commit
e7b1f87665
|
@ -2956,11 +2956,13 @@ public class Configuration implements Iterable<Map.Entry<String,String>>,
|
||||||
// methods that allow non-strings to be put into configurations are removed,
|
// methods that allow non-strings to be put into configurations are removed,
|
||||||
// we could replace properties with a Map<String,String> and get rid of this
|
// we could replace properties with a Map<String,String> and get rid of this
|
||||||
// code.
|
// code.
|
||||||
Map<String,String> result = new HashMap<String,String>();
|
Properties props = getProps();
|
||||||
for(Map.Entry<Object,Object> item: getProps().entrySet()) {
|
Map<String, String> result = new HashMap<>();
|
||||||
if (item.getKey() instanceof String &&
|
synchronized (props) {
|
||||||
item.getValue() instanceof String) {
|
for (Map.Entry<Object, Object> item : props.entrySet()) {
|
||||||
|
if (item.getKey() instanceof String && item.getValue() instanceof String) {
|
||||||
result.put((String) item.getKey(), (String) item.getValue());
|
result.put((String) item.getKey(), (String) item.getValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result.entrySet().iterator();
|
return result.entrySet().iterator();
|
||||||
|
|
|
@ -38,6 +38,7 @@ import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.ConcurrentModificationException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -45,6 +46,7 @@ import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import static java.util.concurrent.TimeUnit.*;
|
import static java.util.concurrent.TimeUnit.*;
|
||||||
|
|
||||||
|
@ -2678,4 +2680,31 @@ public class TestConfiguration {
|
||||||
assertEquals(" prefix >cdata\nsuffix ", conf.get("cdata-whitespace"));
|
assertEquals(" prefix >cdata\nsuffix ", conf.get("cdata-whitespace"));
|
||||||
return conf;
|
return conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConcurrentModificationDuringIteration() throws InterruptedException {
|
||||||
|
Configuration configuration = new Configuration();
|
||||||
|
new Thread(() -> {
|
||||||
|
while (true) {
|
||||||
|
configuration.set(String.valueOf(Math.random()), String.valueOf(Math.random()));
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
AtomicBoolean exceptionOccurred = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
configuration.iterator();
|
||||||
|
} catch (final ConcurrentModificationException e) {
|
||||||
|
exceptionOccurred.set(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
Thread.sleep(1000); //give enough time for threads to run
|
||||||
|
|
||||||
|
assertFalse("ConcurrentModificationException occurred", exceptionOccurred.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue