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

com.gemstone.gemfire.distributed.internal.locks.DLockToken Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2010-2015 Pivotal Software, 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. See accompanying
 * LICENSE file.
 */

package com.gemstone.gemfire.distributed.internal.locks;

import com.gemstone.gemfire.distributed.LeaseExpiredException;
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.Assert;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.gemfire.distributed.internal.*;

import java.util.WeakHashMap;

/**
 * A DistributedLockService contains a collection of DLockToken
 * instances, one for each name in that DistributedLockService for which
 * a lock has ever been requested.  The token identifies whether that
 * name is currently locked, and which distribution manager and thread owns 
 * the lock.
 *
 * @author Dave Monnie
 * @author Kirk Lund
 */
public class DLockToken {
  
  // -------------------------------------------------------------------------
  //   Instance variables
  // -------------------------------------------------------------------------
  
   /** 
    * Lock name for this lock. Logically final but set by fromData.
    */
   private final Object name;
   
   /** 
    * DistributionManager using this lock token. Reference is used to identify
    * local member identity and to {@link DLockService#getLockTimeStamp(DM)}.
    */
   private final DM dm;
   
   /** 
    * LogWriter for this lock token to use in logging.
    */
   private final LogWriterI18n log;
   
   /** 
    * The reply processor id is used to identify the distinct lease which a
    * thread has used to lease this lock.
    */
   private int leaseId = -1;

   /**
    * The absolute time at which the current lease on this lock will expire.  
    * -1 represents a lease which will not expire until explicitly released.
    */
   private long leaseExpireTime = -1;
   
   /** 
    * Remotable identity of thread currently leasing this lock.
    */
   private RemoteThread lesseeThread = null;
   
   /** 
    * Counter that indicates number of times this lock has been re-entered
    * for the current lease.
    */
   private int recursion;
   
  /** 
   * Tracks expired leases so that the leasing thread can report a
   * {@link com.gemstone.gemfire.distributed.LeaseExpiredException}.
   * Keys are threads that have had their lease expire on this lock,
   * but may not yet have noticed.  Would use weak set if available.
   * Entry is removed upon throwing LeaseExpiredException. Protected by
   * synchronization on this lock token.
   */
  private WeakHashMap expiredLeases;
  
  /** 
   * Actual local thread that currently has a lease on this lock.
   */
  private Thread thread;
  
  /** 
   * Number of threads currently using this lock token.
   */
  private int usageCount = 0;
  
  /** 
   * True if this lock token has been destroyed to free up resources.
   */
  private boolean destroyed = false;
  
  /**
   * True if this lock token should be ignored for remote grantor recovery.
   */
  private boolean ignoreForRecovery = false;
  
  // -------------------------------------------------------------------------
  //   Constructors
  // -------------------------------------------------------------------------

  /**
   * Instantiates a new DLockToken for use by {@link DLockService}.
   * 
   * @param dm the DistributionManager for this member
   * @param logWriter the LogWriter to use for logging
   * @param name the identifying name of this lock
   */
  public DLockToken(DM dm, LogWriterI18n logWriter, Object name) {
    this.dm = dm;
    this.log = logWriter;
    this.name = name;
  }
  
  // -------------------------------------------------------------------------
  //   Public accessors
  // -------------------------------------------------------------------------
  
  /**
   * Returns the lock re-entry recursion of the current lease or -1 if there is
   * no current lease. Caller must synchronize on this lock token. 
   * 

* Public because {@link * com.gemstone.gemfire.internal.admin.remote.RemoteDLockInfo} is a caller. * * @return the lock re-entry recursion of the current lease or -1 if none */ public int getRecursion() { return this.recursion; } /** * Returns the name of the actual local thread leasing this lock or null * if there is no lease. Caller must synchronize on this lock token. *

* Public because {@link * com.gemstone.gemfire.internal.admin.remote.RemoteDLockInfo} is a caller. * * @return the name of the actual local thread leasing this lock or null */ public String getThreadName() { return this.thread == null ? null : this.thread.getName(); } /** * Returns the actual local thread leasing this lock or null * if there is no lease. */ public synchronized Thread getThread() { return this.thread; } /** * Returns the absolute time at which the current lease will expire or -1 * if there is no lease. Caller must synchronize on this lock token. *

* Public because {@link * com.gemstone.gemfire.internal.admin.remote.RemoteDLockInfo} is a caller. * * @return the absolute time at which the current lease will expire or -1 */ public long getLeaseExpireTime() { return this.leaseExpireTime; } public int getUsageCount() { return this.usageCount; } // ------------------------------------------------------------------------- // Package accessors // ------------------------------------------------------------------------- /** * Returns the identifying name of this lock. Caller must synchronize on * this lock token if instance was deserialized. * * @return the identifying name of this lock */ Object getName() { return this.name; } /** * Returns the lease id currently used to hold a lease on this lock or -1 * if no thread currently holds this lock. Caller must synchronize on this * token. * * @return the id of the current lease on this lock or -1 if none */ int getLeaseId() { return this.leaseId; } /** * Returns the remotable identity of the thread currently leasing this * lock or null if no thread currently holds this lock. Caller must * synchronize on this lock token. * * @return identity of the thread holding the current lease or null if none */ RemoteThread getLesseeThread() { return this.lesseeThread; } /** * Increment usage count for this lock token. Caller must synchronize on * this lock token. */ void incUsage() { incUsage(1); } /** * Decrement usage count for this lock token. Caller must synchronize on * this lock token. */ void decUsage() { incUsage(-1); } /** * Returns true if the usage count for this lock token is greater than zero. * Caller must synchronize on this lock token. * * @return true if the usage count for this lock token is greater than zero */ boolean isBeingUsed() { return this.usageCount > 0; } // ------------------------------------------------------------------------- // Package operations // ------------------------------------------------------------------------- /** * Destroys this lock token. Caller must synchronize on this lock token. */ synchronized void destroy() { //checkDestroyed(); this.destroyed = true; } /** * Returns the current time in absolute milliseconds for use calculating * lease expiration times. * * @return the current time in absolute milliseconds */ long getCurrentTime() { if (this.dm == null) return -1; return DLockService.getLockTimeStamp(this.dm); } /** * Throws LeaseExpiredException if the calling thread's lease on this lock * previously expired. The expired lease will no longer be tracked after * throwing LeaseExpiredException. Caller must synchronize on this lock * token. * * @throws LeaseExpiredException if calling thread's lease expired */ void throwIfCurrentThreadHadExpiredLease() throws LeaseExpiredException { if (this.expiredLeases == null) { return; } if (this.expiredLeases.containsKey(Thread.currentThread())) { this.expiredLeases.remove(Thread.currentThread()); throw new LeaseExpiredException(LocalizedStrings.DLockToken_THIS_THREADS_LEASE_EXPIRED_FOR_THIS_LOCK.toLocalizedString()); } } /** * Checks the current lease for expiration and returns true if it has * been marked as expired. Caller must synchronize on this lock token. * * @return true if the current lease has been marked as expired */ boolean checkForExpiration() { boolean expired = false; // check if lease exists and lease expire is not MAX_VALUE if (this.leaseId > -1 && this.leaseExpireTime < Long.MAX_VALUE) { long currentTime = getCurrentTime(); if (currentTime > this.leaseExpireTime) { if (this.log.fineEnabled()) { this.log.fine("[checkForExpiration] Expiring token at " + currentTime + ": " + this); } noteExpiredLease(); basicReleaseLock(); expired = true; } } return expired; } /** * Grants new lease to calling thread for this lock token. Synchronizes * on this lock token. * * @param newLeaseExpireTime absolute expiration in millis or Long.MAX_VALUE * @param newLeaseId uniquely identifies the lease for this thread * @param newRecursion recursion count if lock has been re-entered * @param remoteThread identity of the leasing thread * @return true if lease for this lock token is successfully granted */ synchronized boolean grantLock(long newLeaseExpireTime, int newLeaseId, int newRecursion, RemoteThread remoteThread) { Assert.assertTrue(remoteThread != null); Assert.assertTrue(newLeaseId > -1, "Invalid attempt to grant lock with leaseId " + newLeaseId); checkDestroyed(); checkForExpiration(); this.ignoreForRecovery = false; this.leaseExpireTime = newLeaseExpireTime; this.leaseId = newLeaseId; this.lesseeThread = remoteThread; this.recursion = newRecursion; this.thread = Thread.currentThread(); if (this.log.fineEnabled()) { this.log.fine("[DLockToken.grantLock.client] granted " + this); } return true; } /** * Returns true if there's currently a lease on this lock token. * Synchronizes on this lock token. * * @return true if there's currently a lease on this lock token */ synchronized boolean isLeaseHeld() { return this.leaseId > -1; } /** * Returns true if lease on this lock token is held by calling thread or * the specified remote thread. Caller must synchronize on this lock token. * * @param remoteThread remotable identity of thread to check for * @return true if lease is held by calling thread or remote thread */ boolean isLeaseHeldByCurrentOrRemoteThread(RemoteThread remoteThread) { if (isLeaseHeldByCurrentThread()) { return true; } else { return this.lesseeThread != null && remoteThread != null && this.lesseeThread.equals(remoteThread); } } /** * Returns true if lease on this lock token is held by calling thread. * Caller must synchronize on this lock token. * * @return true if lease is held by calling thread */ boolean isLeaseHeldByCurrentThread() { return this.thread == Thread.currentThread(); } /** * Returns true if this lock token should be ignored for grantor recovery. * Caller must synchronize on this lock token. * * @return true if this lock token should be ignored for grantor recovery */ synchronized boolean ignoreForRecovery() { return this.ignoreForRecovery; } /** * Sets whether or not this lock token should be ignored for grantor recovery. * Caller must synchronize on this lock token. * * @param value true if this lock token should be ignored for grantor recovery */ void setIgnoreForRecovery(boolean value) { this.ignoreForRecovery = value; } /** * Releases the current lease on this lock token. Synchronizes on this lock * token. * * @param leaseIdToRelease lease id to release * @param remoteThread identity of thread holding lease * @return true if lock was successfully released */ synchronized boolean releaseLock(int leaseIdToRelease, RemoteThread remoteThread) { return releaseLock(leaseIdToRelease, remoteThread, true); } /** * Releases the current lease on this lock token. Synchronizes on this lock * token. * * @param leaseIdToRelease lease id to release * @param remoteThread identity of thread holding lease * @param decRecursion true if recursion should be decremented * @return true if lock was successfully released */ synchronized boolean releaseLock(int leaseIdToRelease, RemoteThread remoteThread, boolean decRecursion) { if (leaseIdToRelease == -1) return false; if (this.destroyed) { return true; } // return false if not locked by calling thread if (!isLeaseHeld(leaseIdToRelease) || !isLeaseHeldByCurrentOrRemoteThread(remoteThread)) { return false; } // reduce recursion if recursion > 0 else if (decRecursion && getRecursion() > 0) { incRecursion(-1); decUsage(); if (this.log.fineEnabled()) { this.log.fine( "[DLockToken.releaseLock] decremented recursion: " + this); } return true; } // release lock entirely else { basicReleaseLock(); return true; } } /** * Nulls out current lease and decrements usage count. Caller must be * synchronized on this lock token. */ private void basicReleaseLock() { if (this.log.fineEnabled()) { this.log.fine( "[DLockToken.basicReleaseLock] releasing ownership: " + this); } this.leaseId = -1; this.lesseeThread = null; this.leaseExpireTime = -1; this.thread = null; this.recursion = 0; this.ignoreForRecovery = false; decUsage(); } // ------------------------------------------------------------------------- // Private implementation methods // ------------------------------------------------------------------------- /** * Returns true if lease is held using specified lease id. Caller must * synchronize on this lock token. * * @param memberLeaseId lease id used by member * @return true if lease is held using specified lease id */ private boolean isLeaseHeld(int memberLeaseId) { return memberLeaseId == this.leaseId; } /** * Increments or decrements usage count by the specified amount. Caller * must synchronize on this lock token. * * @param amount the amount to inc or dec usage count by */ private void incUsage(int amount) { if (amount < 0 && !this.destroyed) { Assert.assertTrue(this.usageCount-amount >= 0, amount + " cannot be subtracted from usageCount " + this.usageCount); } this.usageCount += amount; } /** * Increments or decrements recursion by the specified amount. Caller must * synchronize on this lock token. * * @param amount the amount to inc or dec recursion by */ private void incRecursion(int amount) { if (amount < 0) { Assert.assertTrue(this.recursion-amount >= 0, amount + " cannot be subtracted from recursion " + this.recursion); } this.recursion += amount; } /** * Throws IllegalStateException if this lock token has been destroyed. * Caller must synchronize on this lock token. * * @throws IllegalStateException if this lock token has been destroyed */ private void checkDestroyed() { if (this.destroyed) { IllegalStateException e = new IllegalStateException(LocalizedStrings.DLockToken_ATTEMPTING_TO_USE_DESTROYED_TOKEN_0.toLocalizedString(this)); throw e; } } /** * Record the token's owning thread as having lost its lease, so it can * throw an exception later if it tries to unlock. A weak reference to the * thread is used. Caller must synchronize on this lock token. */ private void noteExpiredLease() { if (this.log.fineEnabled()) { this.log.fine("[noteExpiredLease] " + this.thread); } if (this.expiredLeases == null) { this.expiredLeases = new WeakHashMap(); } this.expiredLeases.put(this.thread, null); } // ------------------------------------------------------------------------- // java.lang.Object methods // ------------------------------------------------------------------------- /** * Returns a string representation of this object. */ @Override public String toString() { synchronized (this) { return "DLockToken" + "@" + Integer.toHexString(hashCode()) + ", name: " + this.name + ", thread: <" + getThreadName() + ">" + ", recursion: " + this.recursion + ", leaseExpireTime: " + this.leaseExpireTime + ", leaseId: " + this.leaseId + ", ignoreForRecovery: " + this.ignoreForRecovery + ", lesseeThread: " + this.lesseeThread + ", usageCount: " + this.usageCount + ", currentTime: " + getCurrentTime(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy