001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.master.assignment; 019 020import static org.hamcrest.MatcherAssert.assertThat; 021import static org.hamcrest.Matchers.greaterThanOrEqualTo; 022import static org.junit.Assert.assertEquals; 023import static org.junit.Assert.assertFalse; 024import static org.junit.Assert.assertThrows; 025import static org.junit.Assert.assertTrue; 026import static org.mockito.Mockito.mock; 027import static org.mockito.Mockito.verify; 028 029import java.util.concurrent.TimeUnit; 030import java.util.concurrent.atomic.AtomicLong; 031import org.apache.hadoop.hbase.HBaseClassTestRule; 032import org.apache.hadoop.hbase.TableName; 033import org.apache.hadoop.hbase.client.RegionInfo; 034import org.apache.hadoop.hbase.client.RegionInfoBuilder; 035import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException; 036import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility.NoopProcedure; 037import org.apache.hadoop.hbase.testclassification.MasterTests; 038import org.apache.hadoop.hbase.testclassification.SmallTests; 039import org.apache.hadoop.hbase.util.AtomicUtils; 040import org.apache.hadoop.hbase.util.Threads; 041import org.junit.Before; 042import org.junit.ClassRule; 043import org.junit.Test; 044import org.junit.experimental.categories.Category; 045 046@Category({ MasterTests.class, SmallTests.class }) 047public class TestRegionStateNodeLock { 048 049 @ClassRule 050 public static final HBaseClassTestRule CLASS_RULE = 051 HBaseClassTestRule.forClass(TestRegionStateNodeLock.class); 052 053 private final RegionInfo regionInfo = 054 RegionInfoBuilder.newBuilder(TableName.valueOf("test")).build(); 055 056 private RegionStateNodeLock lock; 057 058 @Before 059 public void setUp() { 060 lock = new RegionStateNodeLock(regionInfo); 061 } 062 063 @Test 064 public void testLockByThread() { 065 assertFalse(lock.isLocked()); 066 assertFalse(lock.isLockedBy(Thread.currentThread())); 067 assertThrows(IllegalMonitorStateException.class, () -> lock.unlock()); 068 lock.lock(); 069 assertTrue(lock.isLocked()); 070 assertTrue(lock.isLockedBy(Thread.currentThread())); 071 assertFalse(lock.isLockedBy(new Object())); 072 // reentrant 073 assertTrue(lock.tryLock()); 074 lock.unlock(); 075 assertTrue(lock.isLocked()); 076 lock.unlock(); 077 assertFalse(lock.isLocked()); 078 } 079 080 @Test 081 public void testLockByProc() throws ProcedureSuspendedException { 082 NoopProcedure<?> proc = new NoopProcedure<Void>(); 083 assertFalse(lock.isLocked()); 084 assertFalse(lock.isLockedBy(proc)); 085 assertThrows(IllegalMonitorStateException.class, () -> lock.unlock(proc)); 086 // here we do not need wake up 087 lock.lock(proc, null); 088 assertTrue(lock.isLocked()); 089 assertTrue(lock.isLockedBy(proc)); 090 // reentrant 091 assertTrue(lock.tryLock(proc)); 092 lock.unlock(proc); 093 assertTrue(lock.isLocked()); 094 assertTrue(lock.isLockedBy(proc)); 095 lock.unlock(proc); 096 assertFalse(lock.isLocked()); 097 assertFalse(lock.isLockedBy(proc)); 098 } 099 100 @Test 101 public void testLockProcThenThread() throws ProcedureSuspendedException { 102 NoopProcedure<?> proc = new NoopProcedure<Void>(); 103 assertFalse(lock.isLocked()); 104 lock.lock(proc, null); 105 assertFalse(lock.tryLock()); 106 assertThrows(IllegalMonitorStateException.class, () -> lock.unlock()); 107 long startNs = System.nanoTime(); 108 new Thread(() -> { 109 Threads.sleepWithoutInterrupt(2000); 110 lock.unlock(proc); 111 }).start(); 112 lock.lock(); 113 long costNs = System.nanoTime() - startNs; 114 assertThat(TimeUnit.NANOSECONDS.toMillis(costNs), greaterThanOrEqualTo(1800L)); 115 assertTrue(lock.isLocked()); 116 lock.unlock(); 117 assertFalse(lock.isLocked()); 118 } 119 120 @Test 121 public void testLockThreadThenProc() throws ProcedureSuspendedException { 122 lock.lock(); 123 NoopProcedure<?> proc = new NoopProcedure<Void>(); 124 Runnable wakeUp = mock(Runnable.class); 125 assertThrows(ProcedureSuspendedException.class, () -> lock.lock(proc, wakeUp)); 126 lock.unlock(); 127 // make sure that we have woken up the procedure, and the lock has been passed 128 verify(wakeUp).run(); 129 assertTrue(lock.isLockedBy(proc)); 130 } 131 132 @Test 133 public void testLockMultiThread() throws InterruptedException { 134 int nThreads = 10; 135 AtomicLong concurrency = new AtomicLong(0); 136 AtomicLong maxConcurrency = new AtomicLong(0); 137 Thread[] threads = new Thread[nThreads]; 138 for (int i = 0; i < nThreads; i++) { 139 threads[i] = new Thread(() -> { 140 for (int j = 0; j < 1000; j++) { 141 lock.lock(); 142 try { 143 long c = concurrency.incrementAndGet(); 144 AtomicUtils.updateMax(maxConcurrency, c); 145 concurrency.decrementAndGet(); 146 } finally { 147 lock.unlock(); 148 } 149 Threads.sleepWithoutInterrupt(1); 150 } 151 }); 152 } 153 for (Thread t : threads) { 154 t.start(); 155 } 156 for (Thread t : threads) { 157 t.join(); 158 } 159 assertEquals(0, concurrency.get()); 160 assertEquals(1, maxConcurrency.get()); 161 assertFalse(lock.isLocked()); 162 } 163}