/*
* 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.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.junit.Assert;
import org.junit.Test;
import com.cinchapi.concourse.server.concurrent.LockService;
import com.cinchapi.concourse.server.concurrent.Token;
import com.cinchapi.concourse.test.ConcourseBaseTest;
import com.cinchapi.concourse.util.TCollections;
import com.cinchapi.concourse.util.TestData;
import com.google.common.base.Throwables;
import com.google.common.collect.Sets;
/**
* Unit tests for {@link LockService}.
*
* @author Jeff Nelson
*/
public class LockServiceTest extends ConcourseBaseTest {
private LockService lockService;
@Override
protected void beforeEachTest() {
lockService = LockService.create();
}
@Test
public void testLockServiceDoesNotEvictLocksThatAreBeingUsedWithHighConcurrencyAndDifferentActions()
throws InterruptedException {
int clients = TestData.getScaleCount();
final AtomicBoolean done = new AtomicBoolean(false);
final AtomicBoolean failed = new AtomicBoolean(false);
final Set<String> keys = Sets.newHashSet();
while (keys.size() < clients) {
keys.add(TestData.getString());
}
final Set<Long> records = Sets.newHashSet();
while (records.size() < clients) {
records.add(TestData.getLong());
}
Runnable r = new Runnable() {
@Override
public void run() {
while (!done.get()) {
try {
String key = TCollections.getRandomElement(keys);
long record = TCollections.getRandomElement(records);
ReadLock readLock = lockService.getReadLock(key, record);
readLock.lock();
readLock.unlock();
}
catch (IllegalMonitorStateException e) {
e.printStackTrace();
done.set(true);
failed.set(true);
}
}
}
};
for (int i = 0; i < clients; i++) {
new Thread(r).start();
}
Thread.sleep(TestData.getScaleCount() * 10);
done.set(true);
Assert.assertFalse(failed.get());
}
@Test
public void testLockServiceDoesNotEvictLocksThatAreBeingUsedWithHighConcurrency()
throws InterruptedException {
int clients = TestData.getScaleCount();
final AtomicBoolean done = new AtomicBoolean(false);
final AtomicBoolean failed = new AtomicBoolean(false);
Runnable r = new Runnable() {
@Override
public void run() {
while (!done.get()) {
try {
ReadLock readLock = lockService.getReadLock("bar", 1);
readLock.lock();
readLock.unlock();
}
catch (IllegalMonitorStateException e) {
done.set(true);
failed.set(true);
e.printStackTrace();
}
}
}
};
for (int i = 0; i < clients; i++) {
new Thread(r).start();
}
Thread.sleep(TestData.getScaleCount() * 10);
done.set(true);
Assert.assertFalse(failed.get());
}
@Test
public void testLockServiceDoesNotEvictLocksThatAreBeingUsedEvenWithSomeDelay() {
final AtomicBoolean wait0 = new AtomicBoolean(true);
final AtomicBoolean wait1 = new AtomicBoolean(true);
final AtomicBoolean done = new AtomicBoolean(false);
final AtomicBoolean failed = new AtomicBoolean(false);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
ReadLock readLock = lockService.getReadLock("foo", 1);
readLock.lock();
wait0.set(false);
while (wait1.get()) {
continue;
}
Thread.sleep(TestData.getScaleCount() * 4);
readLock.unlock();
}
catch (IllegalMonitorStateException e) {
failed.set(true);
e.printStackTrace();
}
catch (InterruptedException e) {
throw Throwables.propagate(e);
}
finally {
done.set(true);
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
while (wait0.get()) {
continue;
}
ReadLock readLock = lockService.getReadLock("foo", 1);
readLock.lock();
readLock.unlock();
wait1.set(false);
}
});
t1.start();
t2.start();
while (!done.get()) {
continue;
}
Assert.assertFalse(failed.get());
}
@Test
public void testLockServiceDoesNotEvictLocksThatAreBeingUsed()
throws InterruptedException {
final AtomicBoolean done = new AtomicBoolean(false);
final AtomicBoolean passed = new AtomicBoolean(true);
Thread a = new Thread(new Runnable() {
@Override
public void run() {
while (!done.get()) {
try {
ReadLock readLock = lockService.getReadLock("foo", 1);
readLock.lock();
readLock.unlock();
}
catch (IllegalMonitorStateException e) {
e.printStackTrace();
passed.set(false);
done.set(true);
break;
}
}
}
});
Thread b = new Thread(new Runnable() {
@Override
public void run() {
while (!done.get()) {
try {
WriteLock writeLock = lockService.getWriteLock("foo", 1);
writeLock.lock();
writeLock.unlock();
}
catch (IllegalMonitorStateException e) {
e.printStackTrace();
passed.set(false);
done.set(true);
break;
}
}
}
});
Thread c = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(TestData.getScaleCount() * 10);
done.set(true);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
});
a.start();
b.start();
TestData.getScaleCount(); // make sure that a and b start first
c.start();
a.join();
b.join();
c.join();
Assert.assertTrue(passed.get());
}
@Test
public void testExclusiveWriteLockForUpgradedToken() throws InterruptedException{
Token token = Token.wrap(TestData.getLong());
token.upgrade();
final WriteLock write2 = lockService.getWriteLock(token);
write2.lock();
Thread b = new Thread(new Runnable(){
@Override
public void run() {
Assert.assertFalse(write2.tryLock());
}
});
b.start();
b.join();
}
}