/* * Copyright 2014 Higher Frequency Trading * * http://www.higherfrequencytrading.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. */ package net.openhft.lang.io; import java.util.concurrent.TimeUnit; /** * Created by peter.lawrey on 03/08/14. */ public class OffHeapReadWriteLock { private final Bytes bytes; private final long offset; private final ReadLock readLock; private final WriteLock writeLock; public OffHeapReadWriteLock(Bytes bytes, long offset) { this.bytes = bytes; this.offset = offset; this.readLock = new ReadLock(); this.writeLock = new WriteLock(); } long getLock() { return bytes.readVolatileLong(offset); } boolean trySetLock(long lock0, long lock2) { return bytes.compareAndSwapLong(offset, lock0, lock2); } static final String[] RW_MODES = {"none", "read", "write"}; public ReadLock readLock() { return readLock; } public WriteLock writeLock() { return writeLock; } class ReadLock implements OffHeapLock { @Override public boolean tryLock() { long lock = getLock(); int rwMode = (int) (lock >>> 56) & 0x3; if (rwMode < 2) { long lock2 = (lock | (1L << 56)) + (1 << 16); return trySetLock(lock, lock2); } return false; } @Override public void busyLock() { busyLock(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } @Override public boolean busyLock(long time, TimeUnit timeUnit) { if (tryLock()) return true; addWaitingReader(+1); try { long end = System.nanoTime() + timeUnit.convert(time, TimeUnit.NANOSECONDS); do { if (tryLock()) return true; Thread.yield(); } while (System.nanoTime() < end); } finally { addWaitingReader(-1); } return false; } void addWaitingReader(int add) { for(;;) { long lock = getLock(); int readerWaiting = (int) lock & 0xFFFF; int readerWaiting2 = readerWaiting + add; if (readerWaiting2 >= 1 << 16 || readerWaiting2 < 0) // todo error state return; if (trySetLock(lock, (lock & ~0xFFFF) + readerWaiting2)) return; } } @Override public void unlock() { for (; ; ) { long lock = getLock(); long lock2 = lock - (1 << 16); if (trySetLock(lock, nextMode(lock2))) return; } } } public String toString() { long lock = getLock(); int rwMode = (int) (lock >>> 56) & 0x3; int writerCount = (int) (lock >>> 48) & 0xFF; int writerWaiting = (int) (lock >>> 32) & 0xFFFF; int readerCount = (int) (lock >>> 16) & 0xFFFF; int readerWaiting = (int) lock & 0xFFFF; return RW_MODES[rwMode] + " w:" + writerCount + "/" + writerWaiting + " r:" + readerCount + "/" + readerWaiting; } private long nextMode(long lock) { int rwMode = (int) (lock >>> 56) & 0x3; // int writerCount = (int) (lock >>> 48) & 0xFF; int writerWaiting = (int) (lock >>> 32) & 0xFFFF; int readerCount = (int) (lock >>> 16) & 0xFFFF; // int readerWaiting = (int) lock & 0xFFFF; if ((rwMode == 1 && readerCount == 0) || (rwMode == 2 && writerWaiting == 0)) return lock & (~0L >>> 8); // clear mode. return lock; } class WriteLock implements OffHeapLock{ @Override public boolean tryLock() { long lock = getLock(); int rwMode = (int) (lock >>> 56) & 0x3; int writerCount = (int) (lock >>> 48) & 0xFF; if ((rwMode & 1) == 0 && ( writerCount == 0)) { long lock2 = (lock | (2L << 56)) + (1L << 48); return trySetLock(lock, lock2); } return false; } @Override public void busyLock() { busyLock(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } @Override public boolean busyLock(long time, TimeUnit timeUnit) { if (tryLock()) return true; addWaitingWriter(+1); try { long end = System.nanoTime() + timeUnit.convert(time, TimeUnit.NANOSECONDS); do { if (tryLock()) return true; Thread.yield(); } while (System.nanoTime() < end); } finally { addWaitingWriter(-1); } return false; } void addWaitingWriter(int add) { for(;;) { long lock = getLock(); int writerWaiting = (int) (lock >>> 32) & 0xFFFF; int writerWaiting2 = writerWaiting + add; if (writerWaiting2 >= 1 << 16 || writerWaiting2 < 0) // todo error state return; if (trySetLock(lock, (lock & ~0xFFFF00000000L) + ((long) writerWaiting2 << 32))) return; } } @Override public void unlock() { for (; ; ) { long lock = getLock(); long lock2 = lock - (1L << 48); if (trySetLock(lock, nextMode(lock2))) return; } } } }