/* * 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); } }