All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.concurrent.lock.LockResourceImpl Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * Copyright (c) 2008-2020, Hazelcast, Inc. All Rights Reserved.
 *
 * 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.hazelcast.concurrent.lock;

import com.hazelcast.concurrent.lock.operations.AwaitOperation;
import com.hazelcast.nio.ObjectDataInput;
import com.hazelcast.nio.ObjectDataOutput;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.nio.serialization.IdentifiedDataSerializable;
import com.hazelcast.util.Clock;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.hazelcast.concurrent.lock.LockDataSerializerHook.F_ID;
import static com.hazelcast.concurrent.lock.LockDataSerializerHook.LOCK_RESOURCE;
import static com.hazelcast.util.MapUtil.createHashMap;
import static com.hazelcast.util.SetUtil.createHashSet;

final class LockResourceImpl implements IdentifiedDataSerializable, LockResource {

    private Data key;
    private String owner;
    private long threadId;
    private long referenceId;
    private int lockCount;
    private long expirationTime = -1;
    private long acquireTime = -1L;
    private boolean transactional;
    private boolean blockReads;
    private boolean local;
    private Map waiters;
    private Set conditionKeys;
    private List expiredAwaitOps;
    private LockStoreImpl lockStore;

    // version is stored locally
    // and incremented by 1 for each lock and extendLease operation
    private transient int version;

    public LockResourceImpl() {
    }

    public LockResourceImpl(Data key, LockStoreImpl lockStore) {
        this.key = key;
        this.lockStore = lockStore;
    }

    @Override
    public Data getKey() {
        return key;
    }

    @Override
    public boolean isLocked() {
        return lockCount > 0;
    }

    @Override
    public boolean isLockedBy(String owner, long threadId) {
        return (this.threadId == threadId && owner != null && owner.equals(this.owner));
    }

    boolean lock(String owner, long threadId, long referenceId, long leaseTime, boolean transactional,
                 boolean blockReads, boolean local) {
        if (lockCount == 0) {
            this.owner = owner;
            this.threadId = threadId;
            this.referenceId = referenceId;
            lockCount = 1;
            acquireTime = Clock.currentTimeMillis();
            setExpirationTime(leaseTime);
            this.transactional = transactional;
            this.blockReads = blockReads;
            this.local = local;
            return true;
        } else if (isLockedBy(owner, threadId)) {
            if (!transactional && !local && this.referenceId == referenceId) {
                return true;
            }
            this.referenceId = referenceId;
            lockCount++;
            setExpirationTime(leaseTime);
            this.transactional = transactional;
            this.blockReads = blockReads;
            this.local = local;
            return true;
        }
        return false;
    }

    /**
     * This method is used to extend the already locked resource in the prepare phase of the transactions.
     * It also marks the resource true to block reads.
     *
     * @param caller
     * @param threadId
     * @param leaseTime
     * @return
     */
    boolean extendLeaseTime(String caller, long threadId, long leaseTime) {
        if (!isLockedBy(caller, threadId)) {
            return false;
        }
        this.blockReads = true;
        if (expirationTime < Long.MAX_VALUE) {
            setExpirationTime(expirationTime - Clock.currentTimeMillis() + leaseTime);
        }
        return true;
    }

    private void setExpirationTime(long leaseTime) {
        version++;
        if (leaseTime < 0) {
            expirationTime = Long.MAX_VALUE;
            lockStore.cancelEviction(key);
        } else {
            expirationTime = Clock.currentTimeMillis() + leaseTime;
            if (expirationTime < 0) {
                expirationTime = Long.MAX_VALUE;
                lockStore.cancelEviction(key);
            } else {
                lockStore.scheduleEviction(key, version, leaseTime);
            }
        }
    }

    boolean unlock(String owner, long threadId, long referenceId) {
        if (lockCount == 0) {
            return false;
        }

        if (!isLockedBy(owner, threadId)) {
            return false;
        }

        if (!this.transactional && !this.local && this.referenceId == referenceId) {
            return true;
        }

        this.referenceId = referenceId;
        lockCount--;
        if (lockCount == 0) {
            clear();
        }
        return true;
    }

    boolean canAcquireLock(String caller, long threadId) {
        return lockCount == 0 || getThreadId() == threadId && getOwner().equals(caller);
    }

    void addAwait(String conditionId, String caller, long threadId) {
        if (waiters == null) {
            waiters = createHashMap(2);
        }

        WaitersInfo condition = waiters.get(conditionId);
        if (condition == null) {
            condition = new WaitersInfo(conditionId);
            waiters.put(conditionId, condition);
        }
        condition.addWaiter(caller, threadId);
    }

    void removeAwait(String conditionId, String caller, long threadId) {
        if (waiters == null) {
            return;
        }

        WaitersInfo condition = waiters.get(conditionId);
        if (condition == null) {
            return;
        }

        condition.removeWaiter(caller, threadId);
        if (!condition.hasWaiter()) {
            waiters.remove(conditionId);
        }
    }

    /**
     * Signal a waiter.
     * 

* We need to pass objectName because the name in {#objectName} is unrealible. * * @param conditionId * @param maxSignalCount * @param objectName * @see InternalLockNamespace */ public void signal(String conditionId, int maxSignalCount, String objectName) { if (waiters == null) { return; } Set waiters; WaitersInfo condition = this.waiters.get(conditionId); if (condition == null) { return; } else { waiters = condition.getWaiters(); } if (waiters == null) { return; } Iterator iterator = waiters.iterator(); for (int i = 0; iterator.hasNext() && i < maxSignalCount; i++) { WaitersInfo.ConditionWaiter waiter = iterator.next(); ConditionKey signalKey = new ConditionKey(objectName, key, conditionId, waiter.getCaller(), waiter.getThreadId()); registerSignalKey(signalKey); iterator.remove(); } if (!condition.hasWaiter()) { this.waiters.remove(conditionId); } } private void registerSignalKey(ConditionKey conditionKey) { if (conditionKeys == null) { conditionKeys = new HashSet(); } conditionKeys.add(conditionKey); } ConditionKey getSignalKey() { Set keys = conditionKeys; if (isNullOrEmpty(keys)) { return null; } return keys.iterator().next(); } void removeSignalKey(ConditionKey conditionKey) { if (conditionKeys != null) { conditionKeys.remove(conditionKey); } } boolean hasSignalKey(ConditionKey conditionKey) { if (conditionKeys == null) { return false; } return conditionKeys.contains(conditionKey); } void registerExpiredAwaitOp(AwaitOperation awaitResponse) { if (expiredAwaitOps == null) { expiredAwaitOps = new LinkedList(); } expiredAwaitOps.add(awaitResponse); } AwaitOperation pollExpiredAwaitOp() { List ops = expiredAwaitOps; if (isNullOrEmpty(ops)) { return null; } Iterator iterator = ops.iterator(); AwaitOperation awaitResponse = iterator.next(); iterator.remove(); return awaitResponse; } void clear() { threadId = 0; lockCount = 0; owner = null; referenceId = 0L; expirationTime = 0; acquireTime = -1L; cancelEviction(); version = 0; transactional = false; blockReads = false; local = false; } void cancelEviction() { lockStore.cancelEviction(key); } boolean isRemovable() { return !isLocked() && isNullOrEmpty(waiters) && isNullOrEmpty(expiredAwaitOps) && isNullOrEmpty(conditionKeys); } @Override public String getOwner() { return owner; } @Override public boolean isTransactional() { return transactional; } /** * Local locks are local to the partition and replicaIndex where they have been acquired. * That is the reason they are removed on any partition migration on the destination. * * @returns true if the lock is local, false otherwise */ @Override public boolean isLocal() { return local; } @Override public boolean shouldBlockReads() { return blockReads; } @Override public long getThreadId() { return threadId; } @Override public int getLockCount() { return lockCount; } @Override public long getAcquireTime() { return acquireTime; } @Override public long getRemainingLeaseTime() { if (!isLocked()) { return -1L; } if (expirationTime < 0) { return Long.MAX_VALUE; } long now = Clock.currentTimeMillis(); if (now >= expirationTime) { return 0; } return expirationTime - now; } @Override public long getExpirationTime() { return expirationTime; } @Override public int getVersion() { return version; } void setLockStore(LockStoreImpl lockStore) { this.lockStore = lockStore; } @Override public int getFactoryId() { return F_ID; } @Override public int getId() { return LOCK_RESOURCE; } @Override public void writeData(ObjectDataOutput out) throws IOException { out.writeData(key); out.writeUTF(owner); out.writeLong(threadId); out.writeLong(referenceId); out.writeInt(lockCount); out.writeLong(expirationTime); out.writeLong(acquireTime); out.writeBoolean(transactional); out.writeBoolean(blockReads); int conditionCount = getConditionCount(); out.writeInt(conditionCount); if (conditionCount > 0) { for (WaitersInfo condition : waiters.values()) { condition.writeData(out); } } int signalCount = getSignalCount(); out.writeInt(signalCount); if (signalCount > 0) { for (ConditionKey signalKey : conditionKeys) { out.writeUTF(signalKey.getObjectName()); out.writeUTF(signalKey.getConditionId()); out.writeUTF(signalKey.getUuid()); out.writeLong(signalKey.getThreadId()); } } int expiredAwaitOpsCount = getExpiredAwaitsOpsCount(); out.writeInt(expiredAwaitOpsCount); if (expiredAwaitOpsCount > 0) { for (AwaitOperation op : expiredAwaitOps) { op.writeData(out); } } } private int getExpiredAwaitsOpsCount() { return expiredAwaitOps == null ? 0 : expiredAwaitOps.size(); } private int getSignalCount() { return conditionKeys == null ? 0 : conditionKeys.size(); } private int getConditionCount() { return waiters == null ? 0 : waiters.size(); } @Override public void readData(ObjectDataInput in) throws IOException { key = in.readData(); owner = in.readUTF(); threadId = in.readLong(); referenceId = in.readLong(); lockCount = in.readInt(); expirationTime = in.readLong(); acquireTime = in.readLong(); transactional = in.readBoolean(); blockReads = in.readBoolean(); int len = in.readInt(); if (len > 0) { waiters = createHashMap(len); for (int i = 0; i < len; i++) { WaitersInfo condition = new WaitersInfo(); condition.readData(in); waiters.put(condition.getConditionId(), condition); } } len = in.readInt(); if (len > 0) { conditionKeys = createHashSet(len); for (int i = 0; i < len; i++) { conditionKeys.add(new ConditionKey(in.readUTF(), key, in.readUTF(), in.readUTF(), in.readLong())); } } len = in.readInt(); if (len > 0) { expiredAwaitOps = new ArrayList(len); for (int i = 0; i < len; i++) { AwaitOperation op = new AwaitOperation(); op.readData(in); expiredAwaitOps.add(op); } } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } LockResourceImpl that = (LockResourceImpl) o; if (threadId != that.threadId) { return false; } if (owner != null ? !owner.equals(that.owner) : that.owner != null) { return false; } return true; } @Override public int hashCode() { int result = owner != null ? owner.hashCode() : 0; result = 31 * result + (int) (threadId ^ (threadId >>> 32)); return result; } @Override public String toString() { return "LockResource{" + "owner='" + owner + '\'' + ", threadId=" + threadId + ", lockCount=" + lockCount + ", acquireTime=" + acquireTime + ", expirationTime=" + expirationTime + '}'; } private static boolean isNullOrEmpty(Collection c) { return c == null || c.isEmpty(); } private static boolean isNullOrEmpty(Map m) { return m == null || m.isEmpty(); } void cleanWaitersAndSignalsFor(String uuid) { if (conditionKeys != null) { Iterator iter = conditionKeys.iterator(); while (iter.hasNext()) { ConditionKey conditionKey = iter.next(); if (conditionKey.getUuid().equals(uuid)) { iter.remove(); } } } if (waiters != null) { for (WaitersInfo waitersInfo : waiters.values()) { Iterator iter = waitersInfo.getWaiters().iterator(); while (iter.hasNext()) { WaitersInfo.ConditionWaiter waiter = iter.next(); if (waiter.getCaller().equals(uuid)) { iter.remove(); } } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy