/*
* 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.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
/**
* A service that provides ReadLock and WriteLock instances for a given
* {@link Token}. The locks that are returned from this service can be used to
* lock <em>notions of things</em> that aren't strictly defined in their own
* right (i.e. a {@code key} in a {@code record})
* <p>
* <strong>WARNING</strong>: If the caller requests a lock for a given token,
* but does not attempt to grab it immediately, then it is possible that
* subsequent requests for locks identified by the same token will return
* different instances. This is unlikely to happen in practice, but it is
* recommended that lock grabs happen immediately after lock requests just to be
* safe (e.g <code>LockService.getReadLock(key, record).lock()</code>).
* </p>
*
* @author Jeff Nelson
*/
public class LockService extends AbstractLockService<Token, TokenReadWriteLock> {
/**
* Return a new {@link LockService} instance.
*
* @return the LockService
*/
public static LockService create() {
return new LockService(
new ConcurrentHashMap<Token, TokenReadWriteLock>());
}
/**
* Return a {@link LockService} that does not actually provide any locks.
* This is used in situations where access is guaranteed (or at least
* assumed) to be isolated (e.g. a Transaction) and we need to simulate
* locking for polymorphic consistency.
*
* @return the LockService
*/
public static LockService noOp() {
return NOOP_INSTANCE;
}
/**
* A {@link LockService} that does not actually provide any locks. This is
* used in situations where access is guaranteed (or at least assumed) to be
* isolated (e.g. a Transaction) and we need to simulate locking for
* polymorphic consistency.
*/
private static final LockService NOOP_INSTANCE = new LockService() {
@Override
public ReadLock getReadLock(Token token) {
return Locks.noOpReadLock();
}
@Override
public WriteLock getWriteLock(Token token) {
return Locks.noOpWriteLock();
}
};
private LockService() {/* noop */}
/**
* Construct a new instance.
*
* @param locks
*/
private LockService(ConcurrentHashMap<Token, TokenReadWriteLock> locks) {
super(locks);
}
/**
* Return the ReadLock that is identified by {@code objects}. Every caller
* requesting a lock for {@code token} is guaranteed to get the same
* instance if the lock is currently held by a reader of a writer.
*
* @param objects
* @return the ReadLock
*/
public ReadLock getReadLock(Object... objects) {
return getReadLock(Token.wrap(objects));
}
/**
* Return the WriteLock that is identified by {@code objects}. Every caller
* requesting a lock for {@code token} is guaranteed to get the same
* instance if the lock is currently held by a reader of a writer.
*
* @param objects
* @return the WriteLock
*/
public WriteLock getWriteLock(Object... objects) {
return getWriteLock(Token.wrap(objects));
}
@Override
protected TokenReadWriteLock createLock(Token token) {
return new TokenReadWriteLock(token);
}
}