2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org
.apache
.hadoop
.hbase
.util
;
20 import static org
.junit
.Assert
.assertEquals
;
21 import static org
.junit
.Assert
.assertTrue
;
23 import java
.util
.Arrays
;
25 import java
.util
.Random
;
26 import java
.util
.concurrent
.Callable
;
27 import java
.util
.concurrent
.ConcurrentHashMap
;
28 import java
.util
.concurrent
.ExecutorCompletionService
;
29 import java
.util
.concurrent
.ExecutorService
;
30 import java
.util
.concurrent
.Executors
;
31 import java
.util
.concurrent
.Future
;
32 import java
.util
.concurrent
.TimeUnit
;
33 import java
.util
.concurrent
.locks
.Lock
;
34 import java
.util
.concurrent
.locks
.ReentrantReadWriteLock
;
35 import org
.apache
.hadoop
.hbase
.HBaseClassTestRule
;
36 import org
.apache
.hadoop
.hbase
.testclassification
.MediumTests
;
37 import org
.apache
.hadoop
.hbase
.testclassification
.MiscTests
;
38 import org
.apache
.hadoop
.hbase
.util
.IdReadWriteLock
.ReferenceType
;
39 import org
.junit
.ClassRule
;
40 import org
.junit
.Test
;
41 import org
.junit
.experimental
.categories
.Category
;
42 import org
.junit
.runner
.RunWith
;
43 import org
.junit
.runners
.Parameterized
;
44 import org
.slf4j
.Logger
;
45 import org
.slf4j
.LoggerFactory
;
47 @RunWith(Parameterized
.class)
48 @Category({MiscTests
.class, MediumTests
.class})
49 // Medium as it creates 100 threads; seems better to run it isolated
50 public class TestIdReadWriteLock
{
53 public static final HBaseClassTestRule CLASS_RULE
=
54 HBaseClassTestRule
.forClass(TestIdReadWriteLock
.class);
56 private static final Logger LOG
= LoggerFactory
.getLogger(TestIdReadWriteLock
.class);
58 private static final int NUM_IDS
= 16;
59 private static final int NUM_THREADS
= 128;
60 private static final int NUM_SECONDS
= 15;
62 @Parameterized.Parameter
63 public IdReadWriteLock
<Long
> idLock
;
65 @Parameterized.Parameters
66 public static Iterable
<Object
[]> data() {
67 return Arrays
.asList(new Object
[][] { { new IdReadWriteLock
<Long
>(ReferenceType
.WEAK
) },
68 { new IdReadWriteLock
<Long
>(ReferenceType
.SOFT
) } });
71 private Map
<Long
, String
> idOwner
= new ConcurrentHashMap
<>();
73 private class IdLockTestThread
implements Callable
<Boolean
> {
75 private String clientId
;
77 public IdLockTestThread(String clientId
) {
78 this.clientId
= clientId
;
82 public Boolean
call() throws Exception
{
83 Thread
.currentThread().setName(clientId
);
84 Random rand
= new Random();
85 long endTime
= System
.currentTimeMillis() + NUM_SECONDS
* 1000;
86 while (System
.currentTimeMillis() < endTime
) {
87 long id
= rand
.nextInt(NUM_IDS
);
88 boolean readLock
= rand
.nextBoolean();
90 ReentrantReadWriteLock readWriteLock
= idLock
.getLock(id
);
91 Lock lock
= readLock ? readWriteLock
.readLock() : readWriteLock
.writeLock();
94 int sleepMs
= 1 + rand
.nextInt(4);
95 String owner
= idOwner
.get(id
);
96 if (owner
!= null && LOG
.isDebugEnabled()) {
97 LOG
.debug((readLock ?
"Read" : "Write") + "lock of Id " + id
+ " already taken by "
98 + owner
+ ", we are " + clientId
);
101 idOwner
.put(id
, clientId
);
102 Thread
.sleep(sleepMs
);
107 if (LOG
.isDebugEnabled()) {
108 LOG
.debug("Release " + (readLock ?
"Read" : "Write") + " lock of Id" + id
+ ", we are "
119 public void testMultipleClients() throws Exception
{
120 ExecutorService exec
= Executors
.newFixedThreadPool(NUM_THREADS
);
122 ExecutorCompletionService
<Boolean
> ecs
= new ExecutorCompletionService
<>(exec
);
123 for (int i
= 0; i
< NUM_THREADS
; ++i
)
124 ecs
.submit(new IdLockTestThread("client_" + i
));
125 for (int i
= 0; i
< NUM_THREADS
; ++i
) {
126 Future
<Boolean
> result
= ecs
.take();
127 assertTrue(result
.get());
129 int entryPoolSize
= idLock
.purgeAndGetEntryPoolSize();
130 LOG
.debug("Size of entry pool after gc and purge: " + entryPoolSize
);
131 ReferenceType refType
= idLock
.getReferenceType();
134 // make sure the entry pool will be cleared after GC and purge call
135 assertEquals(0, entryPoolSize
);
138 // make sure the entry pool won't be cleared when JVM memory is enough
139 // even after GC and purge call
140 assertEquals(NUM_IDS
, entryPoolSize
);
147 exec
.awaitTermination(5000, TimeUnit
.MILLISECONDS
);