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

com.tangosol.util.ThreadGateLite Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */

package com.tangosol.util;


import com.oracle.coherence.common.base.Blocking;
import com.oracle.coherence.common.util.Sentry;
import java.util.concurrent.TimeUnit;

import java.util.concurrent.atomic.AtomicReference;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


/**
* ThreadGateLite is a Gate implementation built around the {@link
* ReentrantReadWriteLock}.
*
* @author coh 2010.08.09
*
* @since  Coherence 3.7
*/
public final class ThreadGateLite
        implements Gate
    {
    /**
     * Default constructor.
     */
    public ThreadGateLite()
        {
        this(null);
        }

    /**
     * Construct a ThreadGateLite protected the specified resource.
     *
     * @param resource  the resource
     */
    public ThreadGateLite(R resource)
        {
        f_resource = resource;
        }

    // ---- Gate interface --------------------------------------------------


    @Override
    public Sentry close()
        {
        close(-1L);
        return f_openSentry;
        }

    /**
    * {@inheritDoc}
    */
    @Override
    public boolean close(long cMillis)
        {
        do
            {
            Bar bar = f_atomicBar.get();
            if (bar == null || bar.f_thread == Thread.currentThread())
                {
                long ldtStart = Base.getSafeTimeMillis();
                if (acquireLock(f_rwLock.writeLock(), cMillis))
                    {
                    if (f_atomicBar.get() == bar)
                        {
                        return true;
                        }
                    else
                        {
                        // some thread concurrently barred entry; release the write lock
                        f_rwLock.writeLock().unlock();

                        // account for time spent so far
                        cMillis = adjustWaitTime(cMillis, ldtStart);
                        }
                    }
                else
                    {
                    // we didn't manage to lock in the required time
                    return false;
                    }
                }
            else
                {
                // wait for the bar to be lifted and try again
                cMillis = waitForOpen(bar, cMillis);
                }
            }
        while (cMillis != 0); // keep going if cMillis is positive or -1
        return false;
        }

    @Override
    public Sentry enter()
        {
        enter(-1);
        return f_exitSentry;
        }

    /**
    * {@inheritDoc}
    */
    @Override
    public boolean enter(long cMillis)
        {
        do
            {
            Bar bar = f_atomicBar.get();
            if (bar == null || bar.f_thread == Thread.currentThread()
                    || f_rwLock.getReadHoldCount() > 0)
                {
                // Note: there may be a concurrent barEnter, but we are
                // allowed to complete the attempt to acquire the read lock
                return acquireLock(f_rwLock.readLock(), cMillis);
                }
            else
                {
                // entry is barred by other thread and we are not already in the gate
                cMillis = waitForOpen(bar, cMillis);
                }
            }
        while (cMillis != 0); // keep going if cMillis is positive or -1

        return false;
        }

    /**
    * {@inheritDoc}
    */
    @Override
    public void exit()
        {
        f_rwLock.readLock().unlock();
        }

    /**
    * {@inheritDoc}
    */
    @Override
    public boolean isClosedByCurrentThread()
        {
        return f_rwLock.isWriteLockedByCurrentThread();
        }

    /**
    * {@inheritDoc}
    */
    @Override
    public boolean isClosed()
        {
        return f_rwLock.isWriteLocked();
        }

    /**
    * {@inheritDoc}
    */
    @Override
    public boolean isEnteredByCurrentThread()
        {
        return f_rwLock.getReadHoldCount() > 0;
        }

    /**
    * {@inheritDoc}
    */
    @Override
    public void open()
        {
        Bar bar = f_atomicBar.get();

        // first match all barEntry calls, then close calls
        if (bar == null)
            {
            f_rwLock.writeLock().unlock();
            }
        else if (bar.f_thread != Thread.currentThread())
            {
            // the bar was set by another thread - can't open it;
            // can't open a close either, because for barEntry
            // and close to be in effect at the same time, they must
            // be on the same thread; thus, this open is illegal
            throw new IllegalMonitorStateException(
                    "Gate was not closed by this thread");
            }
        else if (--bar.m_cBarred == 0)
            {
            synchronized (bar)
                {
                f_atomicBar.set(null);
                bar.notifyAll();
                }
            }
        }

    /**
    * {@inheritDoc}
    */
    @Override
    public boolean barEntry(long cMillis)
        {
        do
            {
            Bar bar = f_atomicBar.get();
            if (bar == null)
                {
                long ldtStart = Base.getSafeTimeMillis();
                if (enter(cMillis))
                    {
                    try
                        {
                        if (f_atomicBar.compareAndSet(null, new Bar(Thread.currentThread())))
                            {
                            return true;
                            }

                        cMillis = adjustWaitTime(cMillis, ldtStart);
                        }
                    finally
                        {
                        exit();
                        }
                    }
                else
                    {
                    // time is up and we didn't succeed
                    return false;
                    }
                }
            else if (bar.f_thread == Thread.currentThread())
                {
                ++bar.m_cBarred;
                return true;
                }
            else
                {
                cMillis = waitForOpen(bar, cMillis);
                }
            }
        while (cMillis != 0); // keep going if cMillis is positive or -1

        return false;
        }

    /**
    * {@inheritDoc}
    */
    @Override
    public String toString()
        {
        return "ThreadGateLite{lock=" + f_rwLock.toString()
                + ", bar=" + f_atomicBar.get() + "}";
        }

    // ---- helper methods --------------------------------------------------

    /**
    * Try to acquire the lock within the supplied time interval.
    *
    * @param lock    the lock that should be acquired
    * @param cMillis for how long to attempt to acquire the lock,
    *                pass -1 to wait indefinitely; 0 to return immediately
    *
    * @return true if the lock was acquired, false if not
    */
    private boolean acquireLock(Lock lock, long cMillis)
         {
         try
             {
             if (cMillis < 0)
                 {
                 lock.lock();
                 return true;
                 }
             else if (cMillis == 0)
                 {
                 return lock.tryLock();
                 }
             else
                 {
                 return Blocking.tryLock(lock, cMillis, TimeUnit.MILLISECONDS);
                 }
             }
         catch (InterruptedException e)
             {
             Thread.currentThread().interrupt();
             return false;
             }
         }

    /**
    * Wait for notification that the bar has been lifted
    * completely, i.e. all re-entrant barEntry calls were matched.
    * See {@link #open}.
    *
    * @param bar      the bar that needs to be lifted for this thread
    *                 to proceed (cannot be null)
    * @param cMillis  time to wait or -1 for unlimited wait
    *
    * @return the remaining wait time, or 0 if the wait time has expired
    */
    protected long waitForOpen(Bar bar, long cMillis)
        {
        // Note: re-checking the current bar is necessary here to protect
        //       against a concurrent call to open() and missed notification
        synchronized (bar)
            {
            if (bar == f_atomicBar.get())
                {
                if (!bar.f_thread.isAlive())
                    {
                    f_atomicBar.set(null);
                    bar.notifyAll();
                    }
                else if (cMillis != 0)
                    {
                    long ldtStart = Base.getSafeTimeMillis();
                    Base.wait(bar, cMillis < 0 ? 0 : cMillis);
                    cMillis = adjustWaitTime(cMillis, ldtStart);
                    }
                }
            }

        return cMillis;
        }

    /**
    * Calculate the time remaining from the total time allotted for an operation.
    *
    * @param cMillis   the total time allotted for an operation
    * @param ldtStart  the start of the time interval that have passed
    *
    * @return the remaining wait time in milliseconds. The value may be positive,
    *         zero for no time left or -1 for indefinite wait.
    */
    protected long adjustWaitTime(long cMillis, long ldtStart)
        {
        if (cMillis > 0)
            {
            cMillis = Math.max(0, cMillis - (Base.getSafeTimeMillis() - ldtStart));
            }
        return cMillis;
        }


    // ---- inner classes ---------------------------------------------------

    /**
    * Bar represents the state of the {@link #barEntry bars} placed on this gate.
    */
    protected static class Bar
        {
        // ----- constructors -----------------------------------------------

        /**
         * Construct a Bar for the specified thread.
         *
         * @param thread  the thread to construct a bar for
         */
        protected Bar(Thread thread)
            {
            f_thread  = thread;
            m_cBarred = 1;
            }

        // ----- Object methods ---------------------------------------------

        /**
        * {@inheritDoc}
        */
        public String toString()
            {
            return "Bar{m_thread=" + f_thread + "; m_cBarred=" +  m_cBarred + "}";
            }

        // ----- data members -----------------------------------------------

        // the thread that is barring entry
        protected final Thread f_thread;

        // the count of successful reentrant barEntry calls
        protected int m_cBarred;
        }

    // ---- data members ----------------------------------------------------

    /**
     * The protected resource, or null
     */
    private final R f_resource;

    /**
    * The lock used to control the state of this ThreadGateLite instance.
    */
    private final ReentrantReadWriteLock f_rwLock = new ReentrantReadWriteLock();

    /**
    * The bar used in barEntry operation.
    */
    private final AtomicReference f_atomicBar = new AtomicReference<>();

    /**
     * Sentry to return from {@link #enter} that will {@link #exit} when the sentry is closed.
     */
    protected final Sentry f_exitSentry = new Sentry()
        {
        @Override
        public R getResource()
            {
            return f_resource;
            }

        @Override
        public void close()
            {
            exit();
            }
        };

    /**
     * Sentry to return from {@link #close} that will {@link #open} when the sentry is closed.
     */
    protected final Sentry f_openSentry = new Sentry()
        {
        @Override
        public R getResource()
            {
            return f_resource;
            }

        @Override
        public void close()
            {
            open();
            }
        };
    }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy