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

com.orientechnologies.common.concur.lock.OReadersWriterSpinLock Maven / Gradle / Ivy

/*
 *
 *  *  Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com)
 *  *
 *  *  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.
 *  *
 *  * For more information: http://www.orientechnologies.com
 *
 */

package com.orientechnologies.common.concur.lock;

import com.orientechnologies.common.types.OModifiableInteger;
import com.orientechnologies.orient.core.OOrientShutdownListener;
import com.orientechnologies.orient.core.OOrientStartupListener;
import com.orientechnologies.orient.core.Orient;

import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.AbstractOwnableSynchronizer;
import java.util.concurrent.locks.LockSupport;

/**
 * @author Andrey Lomakin (a.lomakin-at-orientechnologies.com)
 * @since 8/18/14
 */
public class OReadersWriterSpinLock extends AbstractOwnableSynchronizer implements OOrientStartupListener, OOrientShutdownListener {
  private final ODistributedCounter                distributedCounter = new ODistributedCounter();

  private final AtomicReference             tail               = new AtomicReference();
  private volatile ThreadLocal lockHolds          = new InitOModifiableInteger();

  private volatile ThreadLocal              myNode             = new InitWNode();
  private volatile ThreadLocal              predNode           = new ThreadLocal();

  public OReadersWriterSpinLock() {
    final WNode wNode = new WNode();
    wNode.locked = false;

    tail.set(wNode);

    Orient.instance().registerWeakOrientStartupListener(this);
    Orient.instance().registerWeakOrientShutdownListener(this);
  }

  public void acquireReadLock() {
    final OModifiableInteger lHolds = lockHolds.get();

    final int holds = lHolds.intValue();
    if (holds > 0) {
      // we have already acquire read lock
      lHolds.increment();
      return;
    } else if (holds < 0) {
      // write lock is acquired before, do nothing
      return;
    }

    distributedCounter.increment();

    WNode wNode = tail.get();
    while (wNode.locked) {
      distributedCounter.decrement();

      while (wNode.locked && wNode == tail.get()) {
        wNode.waitingReaders.add(Thread.currentThread());

        if (wNode.locked && wNode == tail.get())
          LockSupport.park(this);

        wNode = tail.get();
      }

      distributedCounter.increment();

      wNode = tail.get();
    }

    lHolds.increment();
    assert lHolds.intValue() == 1;
  }

  public void releaseReadLock() {
    final OModifiableInteger lHolds = lockHolds.get();
    final int holds = lHolds.intValue();
    if (holds > 1) {
      lockHolds.get().decrement();
      return;
    } else if (holds < 0) {
      // write lock was acquired before, do nothing
      return;
    }

    distributedCounter.decrement();

    lHolds.decrement();
    assert lHolds.intValue() == 0;
  }

  public void acquireWriteLock() {
    final OModifiableInteger lHolds = lockHolds.get();

    if (lHolds.intValue() < 0) {
      lHolds.decrement();
      return;
    }

    final WNode node = myNode.get();
    node.locked = true;

    final WNode pNode = tail.getAndSet(myNode.get());
    predNode.set(pNode);

    while (pNode.locked) {
      pNode.waitingWriter = Thread.currentThread();

      if (pNode.locked)
        LockSupport.park(this);
    }

    pNode.waitingWriter = null;

    while (!distributedCounter.isEmpty())
      ;

    setExclusiveOwnerThread(Thread.currentThread());

    lHolds.decrement();
    assert lHolds.intValue() == -1;
  }

  public void releaseWriteLock() {
    final OModifiableInteger lHolds = lockHolds.get();

    if (lHolds.intValue() < -1) {
      lHolds.increment();
      return;
    }

    setExclusiveOwnerThread(null);

    final WNode node = myNode.get();
    node.locked = false;

    final Thread waitingWriter = node.waitingWriter;
    if (waitingWriter != null)
      LockSupport.unpark(waitingWriter);

    Thread waitingReader;
    while ((waitingReader = node.waitingReaders.poll()) != null) {
      LockSupport.unpark(waitingReader);
    }

    myNode.set(predNode.get());
    predNode.set(null);

    lHolds.increment();
    assert lHolds.intValue() == 0;
  }

  @Override
  public void onShutdown() {
    lockHolds = null;
    myNode = null;
    predNode = null;
  }

  @Override
  public void onStartup() {
    if (lockHolds == null)
      lockHolds = new ThreadLocal() {
        @Override
        protected OModifiableInteger initialValue() {
          return new OModifiableInteger();
        }
      };

    if (myNode == null)
      myNode = new ThreadLocal() {
        @Override
        protected WNode initialValue() {
          return new WNode();
        }
      };

    if (predNode == null)
      predNode = new ThreadLocal();

  }

  private static final class InitWNode extends ThreadLocal {
    @Override
    protected WNode initialValue() {
      return new WNode();
    }
  }

  private static final class InitOModifiableInteger extends ThreadLocal {
    @Override
    protected OModifiableInteger initialValue() {
      return new OModifiableInteger();
    }
  }

  private final static class WNode {
    private final Queue waitingReaders = new ConcurrentLinkedQueue();

    private volatile boolean    locked         = true;
    private volatile Thread     waitingWriter;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy