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 org.junit.Test; 36 import org.junit.runner.RunWith; 37 import org.junit.runners.JUnit4; 38 39 @RunWith(JUnit4.class) 40 public class DoubleCheckTest { 41 @Test provider_nullPointerException()42 public void provider_nullPointerException() { 43 try { 44 DoubleCheck.provider(null); 45 fail(); 46 } catch (NullPointerException expected) { 47 } 48 } 49 50 @Test lazy_nullPointerException()51 public void lazy_nullPointerException() { 52 try { 53 DoubleCheck.lazy(null); 54 fail(); 55 } catch (NullPointerException expected) { 56 } 57 } 58 59 private static final Provider<Object> DOUBLE_CHECK_OBJECT_PROVIDER = 60 DoubleCheck.provider(Object::new); 61 62 @Test doubleWrapping_provider()63 public void doubleWrapping_provider() { 64 assertThat(DoubleCheck.provider(DOUBLE_CHECK_OBJECT_PROVIDER)) 65 .isSameInstanceAs(DOUBLE_CHECK_OBJECT_PROVIDER); 66 } 67 68 @Test doubleWrapping_lazy()69 public void doubleWrapping_lazy() { 70 assertThat(DoubleCheck.lazy(DOUBLE_CHECK_OBJECT_PROVIDER)) 71 .isSameInstanceAs(DOUBLE_CHECK_OBJECT_PROVIDER); 72 } 73 74 @Test get()75 public void get() throws Exception { 76 int numThreads = 10; 77 ExecutorService executor = Executors.newFixedThreadPool(numThreads); 78 79 final CountDownLatch latch = new CountDownLatch(numThreads); 80 LatchedProvider provider = new LatchedProvider(latch); 81 final Lazy<Object> lazy = DoubleCheck.lazy(provider); 82 83 List<Callable<Object>> tasks = Lists.newArrayListWithCapacity(numThreads); 84 for (int i = 0; i < numThreads; i++) { 85 tasks.add( 86 () -> { 87 latch.countDown(); 88 return lazy.get(); 89 }); 90 } 91 92 List<Future<Object>> futures = executor.invokeAll(tasks); 93 94 assertThat(provider.provisions.get()).isEqualTo(1); 95 Set<Object> results = Sets.newIdentityHashSet(); 96 for (Future<Object> future : futures) { 97 results.add(future.get()); 98 } 99 assertThat(results).hasSize(1); 100 } 101 102 private static class LatchedProvider implements Provider<Object> { 103 final AtomicInteger provisions; 104 final CountDownLatch latch; 105 LatchedProvider(CountDownLatch latch)106 LatchedProvider(CountDownLatch latch) { 107 this.latch = latch; 108 this.provisions = new AtomicInteger(); 109 } 110 111 @Override get()112 public Object get() { 113 if (latch != null) { 114 Uninterruptibles.awaitUninterruptibly(latch); 115 } 116 provisions.incrementAndGet(); 117 return new Object(); 118 } 119 } 120 reentranceWithoutCondition_throwsStackOverflow()121 @Test public void reentranceWithoutCondition_throwsStackOverflow() { 122 final AtomicReference<Provider<Object>> doubleCheckReference = 123 new AtomicReference<>(); 124 Provider<Object> doubleCheck = DoubleCheck.provider(() -> doubleCheckReference.get().get()); 125 doubleCheckReference.set(doubleCheck); 126 try { 127 doubleCheck.get(); 128 fail(); 129 } catch (StackOverflowError expected) {} 130 } 131 reentranceReturningSameInstance()132 @Test public void reentranceReturningSameInstance() { 133 final AtomicReference<Provider<Object>> doubleCheckReference = 134 new AtomicReference<>(); 135 final AtomicInteger invocationCount = new AtomicInteger(); 136 final Object object = new Object(); 137 Provider<Object> doubleCheck = DoubleCheck.provider(() -> { 138 if (invocationCount.incrementAndGet() == 1) { 139 doubleCheckReference.get().get(); 140 } 141 return object; 142 }); 143 doubleCheckReference.set(doubleCheck); 144 assertThat(doubleCheck.get()).isSameInstanceAs(object); 145 } 146 reentranceReturningDifferentInstances_throwsIllegalStateException()147 @Test public void reentranceReturningDifferentInstances_throwsIllegalStateException() { 148 final AtomicReference<Provider<Object>> doubleCheckReference = 149 new AtomicReference<>(); 150 final AtomicInteger invocationCount = new AtomicInteger(); 151 Provider<Object> doubleCheck = DoubleCheck.provider(() -> { 152 if (invocationCount.incrementAndGet() == 1) { 153 doubleCheckReference.get().get(); 154 } 155 return new Object(); 156 }); 157 doubleCheckReference.set(doubleCheck); 158 try { 159 doubleCheck.get(); 160 fail(); 161 } catch (IllegalStateException expected) {} 162 } 163 164 @Test instanceFactoryAsLazyDoesNotWrap()165 public void instanceFactoryAsLazyDoesNotWrap() { 166 Factory<Object> factory = InstanceFactory.create(new Object()); 167 assertThat(DoubleCheck.lazy(factory)).isSameInstanceAs(factory); 168 } 169 } 170