/*
* Copyright (c) 2013-2017 Cinchapi Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.cinchapi.concourse.server.concurrent;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Assert;
import org.junit.Test;
import com.cinchapi.concourse.server.concurrent.ReadWriteSharedLock;
import com.cinchapi.concourse.server.concurrent.Threads;
import com.cinchapi.concourse.test.ConcourseBaseTest;
import com.cinchapi.concourse.time.Time;
import com.cinchapi.concourse.util.TestData;
import com.google.common.collect.Lists;
/**
* Unit tests for {@link ReardWriteSharedLock} objects
*
* @author Jeff Nelson
*/
public class ReadWriteSharedLockTest extends ConcourseBaseTest {
private ReadWriteSharedLock lock;
@Override
public void beforeEachTest() {
lock = new ReadWriteSharedLock();
}
@Test
public void testMultipleConcurrentWriters() {
List<Thread> threads = Lists.newArrayList();
final AtomicBoolean success = new AtomicBoolean(true);
Runnable runnable = new Runnable() {
@Override
public void run() {
if(!lock.writeLock().tryLock()) {
success.set(false);
}
}
};
for (int i = 0; i < TestData.getScaleCount(); i++) {
threads.add(new Thread(runnable));
}
for (Thread thread : threads) {
thread.start();
}
Assert.assertTrue(success.get());
}
@Test
public void testMultipleConcurrentReaders() {
List<Thread> threads = Lists.newArrayList();
final AtomicBoolean success = new AtomicBoolean(true);
Runnable runnable = new Runnable() {
@Override
public void run() {
if(!lock.readLock().tryLock()) {
success.set(false);
}
}
};
for (int i = 0; i < TestData.getScaleCount(); i++) {
threads.add(new Thread(runnable));
}
for (Thread thread : threads) {
thread.start();
}
Assert.assertTrue(success.get());
}
@Test
public void testNoReadersWithMultipleConcurrentWriters() {
List<Thread> threads = Lists.newArrayList();
Runnable runnable = new Runnable() {
@Override
public void run() {
lock.writeLock().lock();
}
};
for (int i = 0; i < TestData.getScaleCount(); i++) {
threads.add(new Thread(runnable));
}
for (Thread thread : threads) {
thread.start();
}
new Thread(new Runnable() {
@Override
public void run() {
Assert.assertFalse(lock.readLock().tryLock());
}
}).start();
}
@Test
public void testNoWritersWithMultipleConcurrentReaders() {
List<Thread> threads = Lists.newArrayList();
Runnable runnable = new Runnable() {
@Override
public void run() {
lock.readLock().lock();
}
};
for (int i = 0; i < TestData.getScaleCount(); i++) {
threads.add(new Thread(runnable));
}
for (Thread thread : threads) {
thread.start();
}
new Thread(new Runnable() {
@Override
public void run() {
Assert.assertFalse(lock.writeLock().tryLock());
}
}).start();
}
@Test
public void testNotifyWhenReadNoLongerBlocked() {
List<Thread> threads = Lists.newArrayList();
final AtomicBoolean unlock = new AtomicBoolean(false);
Runnable runnable = new Runnable() {
@Override
public void run() {
lock.writeLock().lock();
while (!unlock.get()) {
continue;
}
lock.writeLock().unlock();
}
};
for (int i = 0; i < TestData.getScaleCount(); i++) {
threads.add(new Thread(runnable));
}
for (Thread thread : threads) {
thread.start();
}
new Thread(new Runnable() {
@Override
public void run() {
long t1 = Time.now();
lock.readLock().lock();
try {
long elapsed = TimeUnit.MILLISECONDS.convert(Time.now()
- t1, TimeUnit.MICROSECONDS);
Assert.assertTrue(elapsed >= (.80 * 100)); // sleep time is
// imprecise, so
// accept 80%
// accuracy
}
finally {
lock.readLock().unlock();
}
}
}).start();
Threads.sleep(100);
unlock.set(true);
}
@Test
public void testNotifyWhenWriteNoLongerBlocked() {
List<Thread> threads = Lists.newArrayList();
final AtomicBoolean unlock = new AtomicBoolean(false);
Runnable runnable = new Runnable() {
@Override
public void run() {
lock.readLock().lock();
while (!unlock.get()) {
continue;
}
lock.readLock().unlock();
}
};
for (int i = 0; i < TestData.getScaleCount(); i++) {
threads.add(new Thread(runnable));
}
for (Thread thread : threads) {
thread.start();
}
new Thread(new Runnable() {
@Override
public void run() {
long t1 = Time.now();
lock.writeLock().lock();
try {
long elapsed = TimeUnit.MILLISECONDS.convert(Time.now()
- t1, TimeUnit.MICROSECONDS);
Assert.assertTrue(elapsed >= (.80 * 100)); // sleep time is
// imprecise, so
// accept 80%
// accuracy
}
finally {
lock.writeLock().unlock();
}
}
}).start();
Threads.sleep(100);
unlock.set(true);
}
@Test
public void testGetWriteLockCount() {
int count = TestData.getScaleCount();
final AtomicInteger finished = new AtomicInteger(0);
Runnable runnable = new Runnable() {
@Override
public void run() {
lock.writeLock().lock();
finished.incrementAndGet();
}
};
for (int i = 0; i < count; i++) {
new Thread(runnable).start();
}
while (finished.get() < count) {
continue;
}
Assert.assertEquals(lock.getWriteLockCount(), count);
}
@Test
public void testGetWriteHoldCount() {
int count = TestData.getScaleCount();
for (int i = 0; i < count; i++) {
lock.writeLock().lock();
}
Assert.assertEquals(lock.getWriteHoldCount(), count);
}
@Test
public void testGetReadLockCount() {
int count = TestData.getScaleCount();
final AtomicInteger finished = new AtomicInteger(0);
Runnable runnable = new Runnable() {
@Override
public void run() {
lock.readLock().lock();
finished.incrementAndGet();
}
};
for (int i = 0; i < count; i++) {
new Thread(runnable).start();
}
while (finished.get() < count) {
continue;
}
Assert.assertEquals(lock.getReadLockCount(), count);
}
@Test
public void testGetReadHoldCount() {
int count = TestData.getScaleCount();
for (int i = 0; i < count; i++) {
lock.readLock().lock();
}
Assert.assertEquals(lock.getReadHoldCount(), count);
}
@Test
public void testIsWriteLockedByCurrentThread() {
Runnable a = new Runnable() {
@Override
public void run() {
Assert.assertFalse(lock.isWriteLockedByCurrentThread());
}
};
Runnable b = new Runnable() {
@Override
public void run() {
lock.writeLock().lock();
Assert.assertTrue(lock.isWriteLockedByCurrentThread());
}
};
for(int i = 0; i < TestData.getScaleCount(); i++){
Thread t = i %3 == 0 ? new Thread(a) : new Thread(b);
t.start();
}
}
}