(final long iterationDurationNanos, final long ttlNanos)
| 107 | } |
| 108 | |
| 109 | private void run(final long iterationDurationNanos, final long ttlNanos) throws Throwable { |
| 110 | final Supplier<Long> s = new Supplier<Long>() { |
| 111 | private long value; |
| 112 | |
| 113 | // this method is not thread-safe, the returned value of the counter may go down |
| 114 | // if this method is called concurrently from different threads |
| 115 | @Override |
| 116 | public Long get() { |
| 117 | return ++value; |
| 118 | } |
| 119 | }; |
| 120 | // The memoizer we are testing |
| 121 | final Supplier<Long> m = memoize(s, ttlNanos); |
| 122 | // Hold the results until all threads terminate |
| 123 | final Collection<Future<Void>> results = new ArrayList<>(); |
| 124 | // Mark the start time, end after iterationDuration |
| 125 | final long beginNanos = System.nanoTime(); |
| 126 | for (int tid = 0; tid < numberOfThreads; tid++) { |
| 127 | results.add(ex.submit(() -> { |
| 128 | // First read from the memoizer. Only one thread will win this race to increment |
| 129 | // 0 to 1, but all threads should read at least 1, if not increment further |
| 130 | Long previousValue = m.get(); |
| 131 | assertThat("previousValue should not be null", previousValue, is(notNullValue())); |
| 132 | assertThat("previousValue should be greater than zero", previousValue, is(greaterThan(0L))); |
| 133 | // Memoizer's ttl was set during previous call (for race winning thread) or |
| 134 | // earlier (for losing threads) but if we delay for at least ttl from now, we |
| 135 | // are sure to get at least one increment if ttl is nonnegative |
| 136 | final long firstSupplierCallNanos = System.nanoTime(); |
| 137 | // using guaranteedIteration this loop is guaranteed to be executed at |
| 138 | // least once regardless of whether we have exceeded time delays |
| 139 | boolean guaranteedIteration = false; |
| 140 | long now; |
| 141 | while ((now = System.nanoTime()) - beginNanos < iterationDurationNanos |
| 142 | || now - firstSupplierCallNanos < ttlNanos || (guaranteedIteration = !guaranteedIteration)) { |
| 143 | // guaranteedIteration will only be set true when the first two timing |
| 144 | // conditions are false, which will allow at least one iteration. After that |
| 145 | // final iteration the boolean will toggle false again to stop the loop. |
| 146 | if (Thread.currentThread().isInterrupted()) { |
| 147 | throw new InterruptedException(); |
| 148 | } |
| 149 | final Long newValue = m.get(); |
| 150 | // check that we never get uninitialized |
| 151 | assertThat("newValue should not be null", newValue, is(notNullValue())); |
| 152 | // check that the counter never goes down // value |
| 153 | assertThat("newValue shuld be larger", newValue, is(not(lessThan(previousValue)))); |
| 154 | previousValue = newValue; |
| 155 | } |
| 156 | return null; |
| 157 | })); |
| 158 | } |
| 159 | /* |
| 160 | * Make sure all the submitted tasks finished correctly |
| 161 | */ |
| 162 | finishAllThreads(results); |
| 163 | /* |
| 164 | * All the writes to s.value field happened-before this read because of all the |
| 165 | * result.get() invocations, so it holds the final/max value returned by any |
| 166 | * thread. We cannot access s.value but it's private, and s.get() will increment |
no test coverage detected