1 /* 2 * Copyright (C) 2016 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static org.junit.Assert.fail; 21 22 import com.google.common.collect.Lists; 23 import com.google.common.collect.Sets; 24 import com.google.common.util.concurrent.Uninterruptibles; 25 import dagger.Lazy; 26 import java.util.List; 27 import java.util.Set; 28 import java.util.concurrent.Callable; 29 import java.util.concurrent.CountDownLatch; 30 import java.util.concurrent.ExecutorService; 31 import java.util.concurrent.Executors; 32 import java.util.concurrent.Future; 33 import java.util.concurrent.atomic.AtomicInteger; 34 import java.util.concurrent.atomic.AtomicReference; 35 import javax.inject.Provider; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 import org.junit.runners.JUnit4; 39 40 @RunWith(JUnit4.class) 41 public class DoubleCheckTest { 42 @Test provider_nullPointerException()43 public void provider_nullPointerException() { 44 try { 45 DoubleCheck.provider(null); 46 fail(); 47 } catch (NullPointerException expected) { 48 } 49 } 50 51 @Test lazy_nullPointerException()52 public void lazy_nullPointerException() { 53 try { 54 DoubleCheck.lazy(null); 55 fail(); 56 } catch (NullPointerException expected) { 57 } 58 } 59 60 private static final Provider<Object> DOUBLE_CHECK_OBJECT_PROVIDER = 61 DoubleCheck.provider(Object::new); 62 63 @Test doubleWrapping_provider()64 public void doubleWrapping_provider() { 65 assertThat(DoubleCheck.provider(DOUBLE_CHECK_OBJECT_PROVIDER)) 66 .isSameInstanceAs(DOUBLE_CHECK_OBJECT_PROVIDER); 67 } 68 69 @Test doubleWrapping_lazy()70 public void doubleWrapping_lazy() { 71 assertThat(DoubleCheck.lazy(DOUBLE_CHECK_OBJECT_PROVIDER)) 72 .isSameInstanceAs(DOUBLE_CHECK_OBJECT_PROVIDER); 73 } 74 75 @Test get()76 public void get() throws Exception { 77 int numThreads = 10; 78 ExecutorService executor = Executors.newFixedThreadPool(numThreads); 79 80 final CountDownLatch latch = new CountDownLatch(numThreads); 81 LatchedProvider provider = new LatchedProvider(latch); 82 final Lazy<Object> lazy = DoubleCheck.lazy(provider); 83 84 List<Callable<Object>> tasks = Lists.newArrayListWithCapacity(numThreads); 85 for (int i = 0; i < numThreads; i++) { 86 tasks.add( 87 () -> { 88 latch.countDown(); 89 return lazy.get(); 90 }); 91 } 92 93 List<Future<Object>> futures = executor.invokeAll(tasks); 94 95 assertThat(provider.provisions.get()).isEqualTo(1); 96 Set<Object> results = Sets.newIdentityHashSet(); 97 for (Future<Object> future : futures) { 98 results.add(future.get()); 99 } 100 assertThat(results).hasSize(1); 101 } 102 103 private static class LatchedProvider implements Provider<Object> { 104 final AtomicInteger provisions; 105 final CountDownLatch latch; 106 LatchedProvider(CountDownLatch latch)107 LatchedProvider(CountDownLatch latch) { 108 this.latch = latch; 109 this.provisions = new AtomicInteger(); 110 } 111 112 @Override get()113 public Object get() { 114 if (latch != null) { 115 Uninterruptibles.awaitUninterruptibly(latch); 116 } 117 provisions.incrementAndGet(); 118 return new Object(); 119 } 120 } 121 reentranceWithoutCondition_throwsStackOverflow()122 @Test public void reentranceWithoutCondition_throwsStackOverflow() { 123 final AtomicReference<Provider<Object>> doubleCheckReference = 124 new AtomicReference<>(); 125 Provider<Object> doubleCheck = DoubleCheck.provider(() -> doubleCheckReference.get().get()); 126 doubleCheckReference.set(doubleCheck); 127 try { 128 doubleCheck.get(); 129 fail(); 130 } catch (StackOverflowError expected) {} 131 } 132 reentranceReturningSameInstance()133 @Test public void reentranceReturningSameInstance() { 134 final AtomicReference<Provider<Object>> doubleCheckReference = 135 new AtomicReference<>(); 136 final AtomicInteger invocationCount = new AtomicInteger(); 137 final Object object = new Object(); 138 Provider<Object> doubleCheck = DoubleCheck.provider(() -> { 139 if (invocationCount.incrementAndGet() == 1) { 140 doubleCheckReference.get().get(); 141 } 142 return object; 143 }); 144 doubleCheckReference.set(doubleCheck); 145 assertThat(doubleCheck.get()).isSameInstanceAs(object); 146 } 147 reentranceReturningDifferentInstances_throwsIllegalStateException()148 @Test public void reentranceReturningDifferentInstances_throwsIllegalStateException() { 149 final AtomicReference<Provider<Object>> doubleCheckReference = 150 new AtomicReference<>(); 151 final AtomicInteger invocationCount = new AtomicInteger(); 152 Provider<Object> doubleCheck = DoubleCheck.provider(() -> { 153 if (invocationCount.incrementAndGet() == 1) { 154 doubleCheckReference.get().get(); 155 } 156 return new Object(); 157 }); 158 doubleCheckReference.set(doubleCheck); 159 try { 160 doubleCheck.get(); 161 fail(); 162 } catch (IllegalStateException expected) {} 163 } 164 165 @Test instanceFactoryAsLazyDoesNotWrap()166 public void instanceFactoryAsLazyDoesNotWrap() { 167 Factory<Object> factory = InstanceFactory.create(new Object()); 168 assertThat(DoubleCheck.lazy(factory)).isSameInstanceAs(factory); 169 } 170 } 171