/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.ignite.internal.processors.cache; import java.util.Collection; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import javax.cache.CacheException; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.IgniteInternalFuture; import org.apache.ignite.internal.IgniteInterruptedCheckedException; import org.apache.ignite.internal.util.typedef.internal.S; import org.jetbrains.annotations.NotNull; /** * */ class CacheLockImpl<K, V> implements Lock { /** Gateway. */ private final GridCacheGateway<K, V> gate; /** */ private final IgniteInternalCache<K, V> delegate; /** Operation context. */ private final CacheOperationContext opCtx; /** */ private final Collection<? extends K> keys; /** */ private int cntr; /** */ private volatile Thread lockedThread; /** * @param gate Gate. * @param delegate Delegate. * @param opCtx Operation context. * @param keys Keys. */ CacheLockImpl(GridCacheGateway<K, V> gate, IgniteInternalCache<K, V> delegate, CacheOperationContext opCtx, Collection<? extends K> keys) { this.gate = gate; this.delegate = delegate; this.opCtx = opCtx; this.keys = keys; } /** {@inheritDoc} */ @Override public void lock() { CacheOperationContext prev = gate.enter(opCtx); try { checkTx(); delegate.lockAll(keys, 0); incrementLockCounter(); } catch (IgniteCheckedException e) { throw new CacheException(e.getMessage(), e); } finally { gate.leave(prev); } } /** * */ private void incrementLockCounter() { assert (lockedThread == null && cntr == 0) || (lockedThread == Thread.currentThread() && cntr > 0); cntr++; lockedThread = Thread.currentThread(); } /** {@inheritDoc} */ @Override public void lockInterruptibly() throws InterruptedException { tryLock(Long.MAX_VALUE, TimeUnit.MILLISECONDS); } /** {@inheritDoc} */ @Override public boolean tryLock() { CacheOperationContext prev = gate.enter(opCtx); try { checkTx(); boolean res = delegate.lockAll(keys, -1); if (res) incrementLockCounter(); return res; } catch (IgniteCheckedException e) { throw new CacheException(e.getMessage(), e); } finally { gate.leave(prev); } } /** {@inheritDoc} */ @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (time <= 0) return tryLock(); CacheOperationContext prev = gate.enter(opCtx); try { checkTx(); IgniteInternalFuture<Boolean> fut = delegate.lockAllAsync(keys, unit.toMillis(time)); try { boolean res = fut.get(); if (res) incrementLockCounter(); return res; } catch (IgniteInterruptedCheckedException e) { if (!fut.cancel()) { if (fut.isDone()) { Boolean res = fut.get(); Thread.currentThread().interrupt(); if (res) incrementLockCounter(); return res; } } if (e.getCause() instanceof InterruptedException) throw (InterruptedException)e.getCause(); throw new InterruptedException(); } } catch (IgniteCheckedException e) { throw new CacheException(e.getMessage(), e); } finally { gate.leave(prev); } } /** {@inheritDoc} */ @Override public void unlock() { CacheOperationContext prev = gate.enter(opCtx); try { if (lockedThread != Thread.currentThread()) { throw new IllegalStateException("Failed to unlock keys (did current thread acquire lock " + "with this lock instance?)."); } assert cntr > 0; cntr--; if (cntr == 0) lockedThread = null; delegate.unlockAll(keys); } catch (IgniteCheckedException e) { throw new CacheException(e.getMessage(), e); } finally { gate.leave(prev); } } /** {@inheritDoc} */ @NotNull @Override public Condition newCondition() { throw new UnsupportedOperationException(); } /** * Verifies there is no ongoing user transaction. * * @throws CacheException */ private void checkTx() throws CacheException { if (delegate.context().tm().inUserTx()) throw new CacheException("Explicit lock can't be acquired within a transaction."); } /** {@inheritDoc} */ @Override public String toString() { return S.toString(CacheLockImpl.class, this); } }