Merge pull request #610 from dangerousben/init-race-condition

Fix FhirContext initialisation race condition.
This commit is contained in:
James Agnew 2017-04-17 18:02:06 -04:00 committed by GitHub
commit 9201692c70
2 changed files with 46 additions and 6 deletions

View File

@ -82,7 +82,6 @@ public class FhirContext {
private Map<String, Class<? extends IBaseResource>> myDefaultTypeForProfile = new HashMap<String, Class<? extends IBaseResource>>(); private Map<String, Class<? extends IBaseResource>> myDefaultTypeForProfile = new HashMap<String, Class<? extends IBaseResource>>();
private volatile Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = Collections.emptyMap(); private volatile Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = Collections.emptyMap();
private boolean myInitialized; private boolean myInitialized;
private boolean myInitializing;
private HapiLocalizer myLocalizer = new HapiLocalizer(); private HapiLocalizer myLocalizer = new HapiLocalizer();
private volatile Map<String, BaseRuntimeElementDefinition<?>> myNameToElementDefinition = Collections.emptyMap(); private volatile Map<String, BaseRuntimeElementDefinition<?>> myNameToElementDefinition = Collections.emptyMap();
private volatile Map<String, RuntimeResourceDefinition> myNameToResourceDefinition = Collections.emptyMap(); private volatile Map<String, RuntimeResourceDefinition> myNameToResourceDefinition = Collections.emptyMap();
@ -644,8 +643,6 @@ public class FhirContext {
} }
private synchronized Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> scanResourceTypes(Collection<Class<? extends IElement>> theResourceTypes) { private synchronized Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> scanResourceTypes(Collection<Class<? extends IElement>> theResourceTypes) {
myInitializing = true;
List<Class<? extends IBase>> typesToScan = new ArrayList<Class<? extends IBase>>(); List<Class<? extends IBase>> typesToScan = new ArrayList<Class<? extends IBase>>();
if (theResourceTypes != null) { if (theResourceTypes != null) {
typesToScan.addAll(theResourceTypes); typesToScan.addAll(theResourceTypes);
@ -844,8 +841,8 @@ public class FhirContext {
return resTypes; return resTypes;
} }
private void validateInitialized() { private synchronized void validateInitialized() {
if (!myInitialized && !myInitializing) { if (!myInitialized) {
scanResourceTypes(toElementList(myResourceTypesToScan)); scanResourceTypes(toElementList(myResourceTypesToScan));
} }
} }

View File

@ -1,7 +1,13 @@
package ca.uhn.fhir.context; package ca.uhn.fhir.context;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender; import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Reference; import org.hl7.fhir.dstu3.model.Reference;
@ -81,4 +87,41 @@ public class FhirContextDstu3Test {
assertEquals(null, genderChild.getBoundEnumType()); assertEquals(null, genderChild.getBoundEnumType());
} }
@Test
public void testInitialisationThreadSafety() {
final FhirContext ctx = FhirContext.forDstu3();
final int numThreads = 4;
final List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<Throwable>());
final ExecutorService threadPool = Executors.newFixedThreadPool(numThreads);
try {
final CountDownLatch threadsReady = new CountDownLatch(numThreads);
final CountDownLatch threadsFinished = new CountDownLatch(numThreads);
for (int i = 0; i < numThreads; i++) {
threadPool.submit(
new Runnable() {
public void run() {
threadsReady.countDown();
try {
threadsReady.await();
ctx.getResourceDefinition("patient");
} catch(final Exception e) {
exceptions.add(e);
}
threadsFinished.countDown();
}
}
);
}
threadsFinished.await();
} catch(final InterruptedException e) {
exceptions.add(e);
} finally {
threadPool.shutdownNow();
}
assertTrue("failed with exception(s): " + exceptions, exceptions.isEmpty());
}
} }