|
19 | 19 | import java.util.Collection;
|
20 | 20 | import java.util.List;
|
21 | 21 | import java.util.Map;
|
22 |
| -import java.util.Properties; |
23 | 22 | import java.util.Queue;
|
24 | 23 | import java.util.UUID;
|
25 | 24 | import java.util.concurrent.Callable;
|
|
33 | 32 | import java.util.concurrent.TimeUnit;
|
34 | 33 | import java.util.concurrent.atomic.AtomicBoolean;
|
35 | 34 | import java.util.concurrent.atomic.AtomicInteger;
|
| 35 | +import java.util.concurrent.atomic.AtomicReference; |
36 | 36 | import java.util.concurrent.locks.Lock;
|
37 | 37 | import java.util.stream.Collectors;
|
38 | 38 | import java.util.stream.IntStream;
|
|
47 | 47 | import org.junit.runners.Parameterized.Parameters;
|
48 | 48 |
|
49 | 49 | import org.springframework.data.redis.connection.RedisConnectionFactory;
|
50 |
| -import org.springframework.data.redis.core.RedisCallback; |
51 |
| -import org.springframework.data.redis.core.RedisOperations; |
52 | 50 | import org.springframework.data.redis.core.StringRedisTemplate;
|
53 | 51 | import org.springframework.integration.redis.rules.RedisAvailable;
|
54 | 52 | import org.springframework.integration.redis.rules.RedisAvailableTests;
|
|
57 | 55 |
|
58 | 56 | import static org.assertj.core.api.Assertions.assertThat;
|
59 | 57 | import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
60 |
| -import static org.mockito.ArgumentMatchers.any; |
61 |
| -import static org.mockito.BDDMockito.willReturn; |
62 |
| -import static org.mockito.Mockito.mock; |
63 | 58 |
|
64 | 59 | /**
|
65 | 60 | * @author Gary Russell
|
66 | 61 | * @author Konstantin Yakimov
|
67 | 62 | * @author Artem Bilan
|
68 | 63 | * @author Vedran Pavic
|
69 | 64 | * @author Unseok Kim
|
| 65 | + * @author Anton Gabov |
70 | 66 | *
|
71 | 67 | * @since 4.0
|
72 | 68 | *
|
@@ -824,21 +820,65 @@ public void earlyWakeUpTest() throws InterruptedException {
|
824 | 820 | registry3.destroy();
|
825 | 821 | }
|
826 | 822 |
|
827 |
| - |
828 |
| - @SuppressWarnings({ "unchecked", "rawtypes" }) |
829 | 823 | @Test
|
830 |
| - public void testUlink() { |
831 |
| - RedisOperations ops = mock(RedisOperations.class); |
832 |
| - Properties props = new Properties(); |
833 |
| - willReturn(props).given(ops).execute(any(RedisCallback.class)); |
834 |
| - props.setProperty("redis_version", "3.0.0"); |
835 |
| - RedisLockRegistry registry = new RedisLockRegistry(mock(RedisConnectionFactory.class), "foo"); |
836 |
| - registry.setRedisLockType(testRedisLockType); |
837 |
| - assertThat(TestUtils.getPropertyValue(registry, "ulinkAvailable", Boolean.class)).isFalse(); |
838 |
| - props.setProperty("redis_version", "4.0.0"); |
839 |
| - registry = new RedisLockRegistry(mock(RedisConnectionFactory.class), "foo"); |
| 824 | + @RedisAvailable |
| 825 | + public void testTwoThreadsRemoveAndObtainSameLockSimultaneously() throws Exception { |
| 826 | + final int TEST_CNT = 200; |
| 827 | + final long EXPIRATION_TIME_MILLIS = 10000; |
| 828 | + final long LOCK_WAIT_TIME_MILLIS = 500; |
| 829 | + final String testKey = "testKey"; |
| 830 | + |
| 831 | + final RedisLockRegistry registry = new RedisLockRegistry(getConnectionFactoryForTest(), this.registryKey); |
840 | 832 | registry.setRedisLockType(testRedisLockType);
|
841 |
| - assertThat(TestUtils.getPropertyValue(registry, "ulinkAvailable", Boolean.class)).isTrue(); |
| 833 | + |
| 834 | + for (int i = 0; i < TEST_CNT; i++) { |
| 835 | + final String lockKey = testKey + i; |
| 836 | + final CountDownLatch latch = new CountDownLatch(1); |
| 837 | + final AtomicReference<Lock> lock1 = new AtomicReference<>(); |
| 838 | + final AtomicReference<Lock> lock2 = new AtomicReference<>(); |
| 839 | + |
| 840 | + Thread thread1 = new Thread(() -> { |
| 841 | + try { |
| 842 | + latch.await(); |
| 843 | + // remove lock |
| 844 | + registry.expireUnusedOlderThan(EXPIRATION_TIME_MILLIS); |
| 845 | + // obtain new lock and try to acquire |
| 846 | + Lock lock = registry.obtain(lockKey); |
| 847 | + lock.tryLock(LOCK_WAIT_TIME_MILLIS, TimeUnit.MILLISECONDS); |
| 848 | + lock.unlock(); |
| 849 | + |
| 850 | + lock1.set(lock); |
| 851 | + } |
| 852 | + catch (InterruptedException ignore) { |
| 853 | + } |
| 854 | + }); |
| 855 | + |
| 856 | + Thread thread2 = new Thread(() -> { |
| 857 | + try { |
| 858 | + latch.await(); |
| 859 | + // remove lock |
| 860 | + registry.expireUnusedOlderThan(EXPIRATION_TIME_MILLIS); |
| 861 | + // obtain new lock and try to acquire |
| 862 | + Lock lock = registry.obtain(lockKey); |
| 863 | + lock.tryLock(LOCK_WAIT_TIME_MILLIS, TimeUnit.MILLISECONDS); |
| 864 | + lock.unlock(); |
| 865 | + |
| 866 | + lock2.set(lock); |
| 867 | + } |
| 868 | + catch (InterruptedException ignore) { |
| 869 | + } |
| 870 | + }); |
| 871 | + |
| 872 | + thread1.start(); |
| 873 | + thread2.start(); |
| 874 | + latch.countDown(); |
| 875 | + thread1.join(); |
| 876 | + thread2.join(); |
| 877 | + |
| 878 | + // locks must be the same! |
| 879 | + assertThat(lock1.get()).isEqualTo(lock2.get()); |
| 880 | + } |
| 881 | + |
842 | 882 | registry.destroy();
|
843 | 883 | }
|
844 | 884 |
|
|
0 commit comments