Home | History | Annotate | Download | only in impl
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html#License
      3 /**
      4  *******************************************************************************
      5  * Copyright (C) 2001-2013, International Business Machines Corporation and    *
      6  * others. All Rights Reserved.                                                *
      7  *******************************************************************************
      8  */
      9 package com.ibm.icu.impl;
     10 
     11 import java.util.concurrent.locks.ReentrantReadWriteLock;
     12 
     13 
     14 /**
     15  * <p>A Reader/Writer lock originally written for ICU service
     16  * implementation. The internal implementation was replaced
     17  * with the JDK's stock read write lock (ReentrantReadWriteLock)
     18  * for ICU 52.</p>
     19  *
     20  * <p>This assumes that there will be little writing contention.
     21  * It also doesn't allow active readers to acquire and release
     22  * a write lock, or deal with priority inversion issues.</p>
     23  *
     24  * <p>Access to the lock should be enclosed in a try/finally block
     25  * in order to ensure that the lock is always released in case of
     26  * exceptions:<br><pre>
     27  * try {
     28  *     lock.acquireRead();
     29  *     // use service protected by the lock
     30  * }
     31  * finally {
     32  *     lock.releaseRead();
     33  * }
     34  * </pre></p>
     35  *
     36  * <p>The lock provides utility methods getStats and clearStats
     37  * to return statistics on the use of the lock.</p>
     38  */
     39 public class ICURWLock {
     40     private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
     41 
     42     private Stats stats = null;
     43 
     44     /**
     45      * Internal class used to gather statistics on the RWLock.
     46      */
     47     public final static class Stats {
     48         /**
     49          * Number of times read access granted (read count).
     50          */
     51         public int _rc;
     52 
     53         /**
     54          * Number of times concurrent read access granted (multiple read count).
     55          */
     56         public int _mrc;
     57 
     58         /**
     59          * Number of times blocked for read (waiting reader count).
     60          */
     61         public int _wrc; // wait for read
     62 
     63         /**
     64          * Number of times write access granted (writer count).
     65          */
     66         public int _wc;
     67 
     68         /**
     69          * Number of times blocked for write (waiting writer count).
     70          */
     71         public int _wwc;
     72 
     73         private Stats() {
     74         }
     75 
     76         private Stats(int rc, int mrc, int wrc, int wc, int wwc) {
     77             this._rc = rc;
     78             this._mrc = mrc;
     79             this._wrc = wrc;
     80             this._wc = wc;
     81             this._wwc = wwc;
     82         }
     83 
     84         private Stats(Stats rhs) {
     85             this(rhs._rc, rhs._mrc, rhs._wrc, rhs._wc, rhs._wwc);
     86         }
     87 
     88         /**
     89          * Return a string listing all the stats.
     90          */
     91         @Override
     92         public String toString() {
     93             return " rc: " + _rc +
     94                 " mrc: " + _mrc +
     95                 " wrc: " + _wrc +
     96                 " wc: " + _wc +
     97                 " wwc: " + _wwc;
     98         }
     99     }
    100 
    101     /**
    102      * Reset the stats.  Returns existing stats, if any.
    103      */
    104     public synchronized Stats resetStats() {
    105         Stats result = stats;
    106         stats = new Stats();
    107         return result;
    108     }
    109 
    110     /**
    111      * Clear the stats (stop collecting stats).  Returns existing stats, if any.
    112      */
    113     public synchronized Stats clearStats() {
    114         Stats result = stats;
    115         stats = null;
    116         return result;
    117     }
    118 
    119     /**
    120      * Return a snapshot of the current stats.  This does not reset the stats.
    121      */
    122     public synchronized Stats getStats() {
    123         return stats == null ? null : new Stats(stats);
    124     }
    125 
    126     /**
    127      * <p>Acquire a read lock, blocking until a read lock is
    128      * available.  Multiple readers can concurrently hold the read
    129      * lock.</p>
    130      *
    131      * <p>If there's a writer, or a waiting writer, increment the
    132      * waiting reader count and block on this.  Otherwise
    133      * increment the active reader count and return.  Caller must call
    134      * releaseRead when done (for example, in a finally block).</p>
    135      */
    136     public void acquireRead() {
    137         if (stats != null) {    // stats is null by default
    138             synchronized (this) {
    139                 stats._rc++;
    140                 if (rwl.getReadLockCount() > 0) {
    141                     stats._mrc++;
    142                 }
    143                 if (rwl.isWriteLocked()) {
    144                     stats._wrc++;
    145                 }
    146             }
    147         }
    148         rwl.readLock().lock();
    149     }
    150 
    151     /**
    152      * <p>Release a read lock and return.  An error will be thrown
    153      * if a read lock is not currently held.</p>
    154      *
    155      * <p>If this is the last active reader, notify the oldest
    156      * waiting writer.  Call when finished with work
    157      * controlled by acquireRead.</p>
    158      */
    159     public void releaseRead() {
    160         rwl.readLock().unlock();
    161     }
    162 
    163     /**
    164      * <p>Acquire the write lock, blocking until the write lock is
    165      * available.  Only one writer can acquire the write lock, and
    166      * when held, no readers can acquire the read lock.</p>
    167      *
    168      * <p>If there are no readers and no waiting writers, mark as
    169      * having an active writer and return.  Otherwise, add a lock to the
    170      * end of the waiting writer list, and block on it.  Caller
    171      * must call releaseWrite when done (for example, in a finally
    172      * block).<p>
    173      */
    174     public void acquireWrite() {
    175         if (stats != null) {    // stats is null by default
    176             synchronized (this) {
    177                 stats._wc++;
    178                 if (rwl.getReadLockCount() > 0 || rwl.isWriteLocked()) {
    179                     stats._wwc++;
    180                 }
    181             }
    182         }
    183         rwl.writeLock().lock();
    184     }
    185 
    186     /**
    187      * <p>Release the write lock and return.  An error will be thrown
    188      * if the write lock is not currently held.</p>
    189      *
    190      * <p>If there are waiting readers, make them all active and
    191      * notify all of them.  Otherwise, notify the oldest waiting
    192      * writer, if any.  Call when finished with work controlled by
    193      * acquireWrite.</p>
    194      */
    195     public void releaseWrite() {
    196         rwl.writeLock().unlock();
    197     }
    198 }
    199