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