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}