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

com.gs.fw.common.mithra.cache.ReadWriteLock Maven / Gradle / Ivy

There is a newer version: 18.1.0
Show newest version
/*
 Copyright 2016 Goldman Sachs.
 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.gs.fw.common.mithra.cache;

import java.util.concurrent.locks.AbstractQueuedSynchronizer;


public class ReadWriteLock
{

    private static final int READER_BITS = 25;
    private static final int READER_MASK = (1 << READER_BITS) - 1;

    private static final int WRITER_MASK = 1 << READER_BITS;
    private static final int PREPARING_TO_GO_LOCAL = 1 << (READER_BITS+1);
    private static final int PREPARING_TO_WRITE_MASK = 1 << (READER_BITS+2);
    private static final int GLOBAL_MASK = 1 << (READER_BITS+4);

    private static final int lockPower = 3;

    private static final int lockCount = 1 << lockPower;

    private static final int LOCK_MASK = (1 << lockPower) - 1;

    private final GlobalSync globalSync = new GlobalSync();
    private final LocalSync[] localSyncs = new LocalSync[lockCount];

    public ReadWriteLock()
    {
        for(int i=0;i= 0)
        {
            localSyncs[remainingPosition].acquire(0);
            remainingPosition--;
        }
        globalSync.acquire(GLOBAL_MASK | PREPARING_TO_WRITE_MASK);
    }

    public void release()
    {
        if (!globalSync.releaseReadOrWrite())
        {
            localSyncs[getLocalSyncIndex()].releaseShared(0);
        }
    }

    private int getLocalSyncIndex()
    {
        return ((int) Thread.currentThread().getId()) & LOCK_MASK;
    }

    /**
     * upgrade a read lock to a write lock. If a lock is already a write lock, this method does nothing.
     *
     * @return true if waited
     *         true if a read lock was upgraded with the possibility of having given away other write locks.
     *         false if the existing lock was a write lock or the upgrade was immediate, that is, no other write locks
     *         were taken
     */
    public boolean upgradeToWriteLock()
    {
        if (globalSync.trySingleReaderUpgrade()) return false;
        slowUpgrade();
        return true;
    }

    private void slowUpgrade()
    {
        this.release();
        this.acquireWriteLock();
    }

    private static final class GlobalSync extends AbstractQueuedSynchronizer
    {
        private Thread readPreparationThread;
        private Thread writePreparationThread;
        private Thread singularReaderThread;
        private int localBias;
        private long buf1,buf2,buf3,buf4; // to prevent cache line conflicts on adjacent locks.

        public GlobalSync()
        {
            this.setState(GLOBAL_MASK);
            buf1 = (long)(Math.random()*1000);
            buf2 = (long)(Math.random()*1000);
            buf3 = (long)(Math.random()*1000);
            buf4 = (long)(Math.random()*1000);

        }

        public long getCacheLineConflict()
        {
            return buf1+buf2+buf3+buf4;
        }

        /*
        acquires is expected to be either GLOBAL_MASK, or GLOBAL_MASK | PREPARING_TO_WRITE
         */
        @Override
        public boolean tryAcquire(int acquires)
        {
            localBias = 0;
            return compareAndSetState(acquires, WRITER_MASK | GLOBAL_MASK) || trySlowAcquire(acquires);
        }

        private boolean trySlowAcquire(int acquires)
        {
            for (; ;)
            {
                int c = getState();
                if ((c & (READER_MASK | PREPARING_TO_GO_LOCAL | WRITER_MASK)) != 0) return false;
                if (acquires == (GLOBAL_MASK | PREPARING_TO_WRITE_MASK))
                {
                    // we've locked all the locals and we're about to lock the global
                    if (compareAndSetState(GLOBAL_MASK | PREPARING_TO_WRITE_MASK, GLOBAL_MASK | WRITER_MASK)) return true;
                }
                else
                {
                    // we're here without having prepared the locals. we can either lock immediately, or prepare to lock
                    if ((c & PREPARING_TO_WRITE_MASK) != 0) return false;
                    if (c == GLOBAL_MASK)
                    {
                        if (compareAndSetState(GLOBAL_MASK, GLOBAL_MASK | WRITER_MASK)) return true;
                    }
                    else
                    {
                        // c must be zero
                        if (compareAndSetState(0, GLOBAL_MASK | PREPARING_TO_WRITE_MASK))
                        {
                            writePreparationThread = Thread.currentThread();
                            return true;
                        }
                    }
                }
            }
        }

        public boolean mustReadLockLocally()
        {
            if (Thread.currentThread() == readPreparationThread)
            {
                readPreparationThread = null;
                return true;
            }
            return false;
        }

        public boolean mustPrepareWriteLock()
        {
            if (Thread.currentThread() == writePreparationThread)
            {
                writePreparationThread = null;
                return true;
            }
            return false;
        }

        public boolean tryFastAcquireShared()
        {
            localBias++;
            if (localBias < 100 && compareAndSetState(GLOBAL_MASK, GLOBAL_MASK | 1))
            {
                singularReaderThread = Thread.currentThread();
                return true;
            }
            return false;
        }

        @Override
        public int tryAcquireShared(int unused)
        {
            for (; ;)
            {
                int c = getState();
                if (c == GLOBAL_MASK || c == 0)
                {
                    if (localBias < 100)
                    {
                        if (compareAndSetState(c, c | 1))
                        {
                            singularReaderThread = Thread.currentThread();
                            return 1;
                        }
                    }
                    else
                    {
                        if (compareAndSetState(c, (c & ~GLOBAL_MASK) | PREPARING_TO_GO_LOCAL))
                        {
                            localBias = 0;
                            readPreparationThread = Thread.currentThread();
                            return 0;
                        }
                    }
                }
                else if ((c & (PREPARING_TO_WRITE_MASK | WRITER_MASK | PREPARING_TO_GO_LOCAL)) == 0)
                {
                    if (compareAndSetState(c, (c & ~GLOBAL_MASK) | PREPARING_TO_GO_LOCAL))
                    {
                        readPreparationThread = Thread.currentThread();
                        return 0;
                    }
                }
                else return -1;
            }
        }

        @Override
        protected final boolean tryRelease(int releases)
        {
            compareAndSetState(GLOBAL_MASK | WRITER_MASK, GLOBAL_MASK);
            return true;
        }

        @Override
        protected final boolean tryReleaseShared(int releases)
        {
            if (releases == 0)
            {
                singularReaderThread = null;
                return compareAndSetState(GLOBAL_MASK | 1, GLOBAL_MASK) || slowReleaseShared();
            }
            else
            {
                releasePrepareForLocal();
                return true;
            }
        }

        private void releasePrepareForLocal()
        {
            for (; ;)
            {
                int c = getState();
                if (compareAndSetState(c, (c & ~PREPARING_TO_GO_LOCAL)))
                    return;
            }
        }

        private boolean slowReleaseShared()
        {
            for (; ;)
            {
                int c = getState();
                if (compareAndSetState(c, (c & ~READER_MASK)))
                    return true;
            }
        }

        public boolean trySingleReaderUpgrade()
        {
            if ((getState() & WRITER_MASK) != 0) return true;
            if (singularReaderThread == Thread.currentThread() &&
                    this.compareAndSetState(GLOBAL_MASK | 1, GLOBAL_MASK | WRITER_MASK))
            {
                singularReaderThread = null;
                return true;
            }
            return false;
        }

        @Override
        public final boolean isHeldExclusively()
        {
            return (getState() & WRITER_MASK) != 0;
        }

        public boolean releaseReadOrWrite()
        {
            if (Thread.currentThread() == singularReaderThread)
            {
                this.releaseShared(0);
                return true;
            }
            if ((getState() & WRITER_MASK) != 0)
            {
                this.release(1);
                return true;
            }
            return false;
        }


    }

    private static final class LocalSync extends AbstractQueuedSynchronizer
    {
        private long buf1,buf2,buf3,buf4,buf5,buf6; // to prevent cache line conflicts on adjacent locks. Size found experimentally on Intel Xeon.

        private LocalSync()
        {
            setState(GLOBAL_MASK);
            buf1 = (long)(Math.random()*1000);
            buf2 = (long)(Math.random()*1000);
            buf3 = (long)(Math.random()*1000);
            buf4 = (long)(Math.random()*1000);
            buf5 = (long)(Math.random()*1000);
            buf6 = (long)(Math.random()*1000);
        }

        @Override
        public boolean tryAcquire(int acquires)
        {
            return compareAndSetState(0, GLOBAL_MASK) || compareAndSetState(GLOBAL_MASK, GLOBAL_MASK);
        }

        public boolean tryAcquireSharedLocal()
        {
            return compareAndSetState(0, 1) || tryAcquireSharedSlowLocal();
        }

        private boolean tryAcquireSharedSlowLocal()
        {
            for (; ;)
            {
                int c = getState();
                if ((c & GLOBAL_MASK) != 0)
                    return false;
                if (compareAndSetState(c, c + 1))
                    return true;
                // Recheck if lost CAS
            }
        }

        public void tryAcquireSharedDeGlobalize()
        {
            if (compareAndSetState(GLOBAL_MASK, 1)) return;
            tryAcquireSharedSlowDeGlobalize();
        }

        private void tryAcquireSharedSlowDeGlobalize()
        {
            for (; ;)
            {
                int c = getState();
                if (compareAndSetState(c, (c & ~GLOBAL_MASK) + 1))
                    return;
                // Recheck if lost CAS
            }
        }

        @Override
        protected final boolean tryReleaseShared(int releases)
        {
            return compareAndSetState(1, 0) || trySlowReleaseShared();
        }

        private boolean trySlowReleaseShared()
        {
            for (; ;)
            {
                int c = getState();
                if (compareAndSetState(c, (c & GLOBAL_MASK) | (c & ~GLOBAL_MASK) - 1))
                    return (c & ~GLOBAL_MASK) == 1;
            }
        }

        public void tryDeglobalize()
        {
            this.compareAndSetState(GLOBAL_MASK, 0);
        }

        public long getCacheLineConflict()
        {
            return buf1+buf2+buf3+buf4+buf5+buf6;
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy