Package com.linkedin.databus.core.util

Source Code of com.linkedin.databus.core.util.RangeBasedReaderWriterLock$LockToken

package com.linkedin.databus.core.util;
/*
*
* Copyright 2013 LinkedIn Corp. 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.
*
*/


import java.util.Arrays;
import java.util.Iterator;
import java.util.PriorityQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;


/**
* brief: A class that allows readers and writers to check out locks for operating on ranges at a time
* @author sdas
*
*/
public class RangeBasedReaderWriterLock {
  public static final String MODULE = RangeBasedReaderWriterLock.class.getName();
  public static final Logger LOG = Logger.getLogger(MODULE);

  private static final long MAX_LOCK_WAIT_MS = 60000;

  public class LockToken implements Comparable<LockToken> {
    protected Range _id;
    private final String _ownerName;
    private final long _createTime;
    private long _lastUpdateTime;
    protected LockToken(Range id, String ownerName) {
      _id = id;
      _ownerName = ownerName;
      _createTime = System.currentTimeMillis();
      _lastUpdateTime = _createTime;
    }

    public Range getRange() {
      return _id;
    }

    public String getOwnerName()
    {
      return _ownerName;
    }

    public void setRangeStart(long newStart)
    {
      _id.start = newStart;
      _lastUpdateTime = System.currentTimeMillis();
    }

    @Override
    public String toString()
    {
      return "{ownerName:" + _ownerName + ", range:" + _id +
             ", created:" + _createTime + ", lastUpdated:" + _lastUpdateTime +
             "}";
    }

    public String toString(BufferPositionParser parser)
    {
      return "{ownerName:" + _ownerName + ", range:" + _id.toString(parser) + "}";
    }

    @Override
    public int compareTo(LockToken o)
    {
      return _id.compareTo(o._id);
    }
  }

  private final PriorityQueue<LockToken> readerRanges;
  private final ReentrantLock mutex;
  private final Condition writesPossible;
  private final Condition readsPossible;

  private Range writerRange;
  private boolean writerIn;
  private boolean writerWaiting;


  public RangeBasedReaderWriterLock() {
    readerRanges = new PriorityQueue<LockToken>(100);
    writerRange = new Range(-1, 0);
    writerIn = false;
    mutex = new ReentrantLock();
    writesPossible = mutex.newCondition();
    readsPossible = mutex.newCondition();

  }

  public LockToken acquireReaderLock(long startOffset, long endOffset, BufferPositionParser parser,
                                     String ownerName)
      throws InterruptedException, TimeoutException
  {
  boolean debug = LOG.isDebugEnabled();
    if (debug)
    {
      LOG.debug("Asked to acquire reader lock from " + parser.toString(startOffset) +
                " to " + parser.toString(endOffset) + " for " + ownerName);
    }

    //LOG.info(Thread.currentThread().getName() + "-Asked to acquire reader lock from " + parser.toString(startOffset) + " to " + parser.toString(endOffset));

    Range readerRange = new Range(startOffset, endOffset);
    mutex.lock();
    try
    {
      boolean timeout = false;
      while (writerIn && writerRange.intersects(readerRange))
      {
        if ( debug )
        {
          LOG.debug("Waiting for reads to be possible since writer is In. Reader Range is :" + readerRange.toString(parser)
            + ". Writer Range is :" + writerRange.toString(parser));
        }
        if (timeout)
          throw new TimeoutException();

        if (!readsPossible.await(MAX_LOCK_WAIT_MS, TimeUnit.MILLISECONDS))
          timeout = true;
        if ( debug )
        {
          LOG.info("Waiting for reads to be possible: coming out of wait");
        }
      }
      LockToken returnVal = new LockToken(readerRange, ownerName);
      readerRanges.add(returnVal);
      if (debug)
      {
        LOG.debug("Returning with reader lock from " + parser.toString(startOffset) + " to " + parser.toString(endOffset));
      }

      return returnVal;
    }
    finally
    {
      mutex.unlock();
    }

  }

  public void shiftReaderLockStart(LockToken lockId, long newStartOffset, BufferPositionParser parser) {
    if (LOG.isDebugEnabled())
    {
      LOG.debug("Being asked to shift reader lock start to "  + parser.toString(newStartOffset) +
                " for " + lockId);
    }
    mutex.lock();
    try
    {
      boolean lockFound = readerRanges.remove(lockId);
      assert lockFound : "lock:" + lockId + "; this:" + toString();
      lockId.setRangeStart(newStartOffset);
      readerRanges.add(lockId);
      writesPossible.signal();
    }
    finally
    {
      mutex.unlock();
    }

  }

  public void shiftReaderLockStartIfWriterWaiting(LockToken lockId, long newStartOffset, BufferPositionParser parser) {
    if (LOG.isDebugEnabled())
    {
      LOG.debug("Being asked to shift reader lock start to "  + parser.toString(newStartOffset) + ";writerWaiting = " + writerWaiting);
    }

    if (writerWaiting) // it is okay to check this value outside the protected section, we don't care if we make a mistake
    {
      mutex.lock();
      try
      {
        readerRanges.remove(lockId);
        lockId.setRangeStart(newStartOffset);
        readerRanges.add(lockId);
        writesPossible.signal();
      }
      finally
      {
        mutex.unlock();
      }
    }
  }

  public void releaseReaderLock(LockToken lockId) {
    if (LOG.isDebugEnabled())
    {
      LOG.debug("Being asked to release reader lock "  + lockId);
    }

    mutex.lock();
    try
    {
      boolean readerLockRemoved = readerRanges.remove(lockId);
      assert readerLockRemoved : "lock:" + lockId + "; this:" + toString();
      writesPossible.signal();
    }
    finally
    {
      mutex.unlock();
    }
  }

  public void acquireWriterLock(long start, long end, BufferPositionParser parser)
         throws InterruptedException, TimeoutException
  {
    long startOffset = parser.address(start);
    long endOffset = parser.address(end);

    boolean debug = LOG.isDebugEnabled();

    if (debug)
      LOG.debug("Acquiring writer lock from " + parser.toString(start) + " to " + parser.toString(end));

    mutex.lock();

    try
    {
        boolean timeout = false;
        while (!readerRanges.isEmpty() &&
                Range.contains(startOffset, endOffset, parser.address(readerRanges.peek()._id.start)))
        {
          if ( debug )
          {
            LOG.debug("Entering wait because reader(s) exist: Writer Range: ["
                + parser.toString(start) + "(Address:" +  parser.toString(startOffset) + ")-"
                + parser.toString(end) + "(Address:" + parser.toString(endOffset) +
                ")]. Nearest Reader Range :" + readerRanges.peek().toString(parser));
          }
          if (timeout)
          {
            LOG.error("timed out waiting for a write lock for [" + parser.toString(start) +
                      "," + parser.toString(end) + "); this: " + this );
            throw new TimeoutException();
          }

          for (LockToken token: readerRanges)
          {
            LOG.info(token.toString(parser));
          }
          writerWaiting = true;
          if (!writesPossible.await(MAX_LOCK_WAIT_MS, TimeUnit.MILLISECONDS))
            timeout = true;

          if ( debug )
            LOG.debug("Writer coming out of wait");
        }
      writerWaiting = false;
      writerIn = true;
      writerRange.start = start;
      writerRange.end = end;
    }
    finally
    {
      mutex.unlock();
    }

  }

  public void releaseWriterLock(BufferPositionParser parser) {
    if (LOG.isDebugEnabled())
    {
      LOG.debug("Releasing writer lock from " + parser.toString(writerRange.start) + " to " + parser.toString(writerRange.end));
    }

    mutex.lock();
    try
    {
      writerIn = false;
      readsPossible.signalAll();
    }
    finally
    {
      mutex.unlock();
    }
  }

  public String toString(BufferPositionParser parser, boolean doSort)
  {
    StringBuilder strBuilder = new StringBuilder();

    strBuilder.append("[writerIn:" + writerIn).append(",WriterWaiting:");
    strBuilder.append(writerWaiting).append(",WriterRange:").append(writerRange.toString(parser));
    strBuilder.append("\nReader Ranges:\n");

    if ( !doSort)
    {
      Iterator<LockToken> it = readerRanges.iterator();
      while (it.hasNext())
      {
        strBuilder.append(it.next().toString(parser)).append("\n");
      }
    } else {
      LockToken[] ranges = new LockToken[readerRanges.size()];
      readerRanges.toArray(ranges);
      Arrays.sort(ranges);
      for (int i = 0 ; i < ranges.length; i++)
      {
        strBuilder.append(ranges[i].toString(parser)).append("\n");
      }
    }
    return strBuilder.toString();
  }



  // package private getters for unit-tests
  PriorityQueue<LockToken> getReaderRanges()
  {
    return readerRanges;
  }

  public boolean isWriterIn()
  {
    return writerIn;
  }

  public boolean isWriterWaiting()
  {
    return writerWaiting;
  }

  public Range getWriterRange()
  {
    return writerRange;
  }

  public int getNumReaders()
  {
    return readerRanges.size();
  }

  @Override
  public String toString()
  {
    mutex.lock();
    try
    {
      return "{readerRanges:" + readerRanges +", writerRange:" + writerRange +
             ", writerIn:" + writerIn + ", writerWaiting:" + writerWaiting + "}";
    }
    finally
    {
      mutex.unlock();
    }
  }
}
TOP

Related Classes of com.linkedin.databus.core.util.RangeBasedReaderWriterLock$LockToken

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.