From 14afd05b4cd45ae9585005aae2cb30d8560036e8 Mon Sep 17 00:00:00 2001
From: Doug Felt
Date: Thu, 20 Jun 2002 02:37:14 +0000
Subject: [PATCH] ICU-1949 ICUService fix ICURWLock to be more concurrent, not
serialize writers
X-SVN-Rev: 8908
---
icu4j/src/com/ibm/icu/impl/ICURWLock.java | 264 ++++++++++++---------
icu4j/src/com/ibm/icu/impl/ICUService.java | 8 +-
2 files changed, 154 insertions(+), 118 deletions(-)
diff --git a/icu4j/src/com/ibm/icu/impl/ICURWLock.java b/icu4j/src/com/ibm/icu/impl/ICURWLock.java
index d626bda9b8..ae22b91b98 100644
--- a/icu4j/src/com/ibm/icu/impl/ICURWLock.java
+++ b/icu4j/src/com/ibm/icu/impl/ICURWLock.java
@@ -1,6 +1,6 @@
package com.ibm.icu.impl;
-// See Allan Holub's 1999 column in JavaWorld.
+// See Allan Holub's 1999 column in JavaWorld, and Doug Lea's code for RWLocks with writer preference.
import java.util.LinkedList;
@@ -26,16 +26,13 @@ import java.util.LinkedList;
* to return statistics on the use of the lock.
*/
public class ICURWLock {
- private LinkedList ww; // list of waiting writers
+ private Object writeLock = new Object();
+ private Object readLock = new Object();
private int wwc; // waiting writers
private int rc; // active readers, -1 if there's an active writer
private int wrc; // waiting readers
- private int stat_rc; // any read access granted
- private int stat_mrc; // multiple read access granted
- private int stat_wrc; // wait for read
- private int stat_wc; // write access granted
- private int stat_wwc; // wait for write
+ private Stats stats = new Stats(); // maybe don't init to start...
/**
* Internal class used to gather statistics on the RWLock.
@@ -44,30 +41,33 @@ public class ICURWLock {
/**
* Number of times read access granted (read count).
*/
- public final int _rc;
+ public int _rc;
/**
* Number of times concurrent read access granted (multiple read count).
*/
- public final int _mrc;
+ public int _mrc;
/**
* Number of times blocked for read (waiting reader count).
*/
- public final int _wrc; // wait for read
+ public int _wrc; // wait for read
/**
* Number of times write access granted (writer count).
*/
- public final int _wc;
+ public int _wc;
/**
* Number of times blocked for write (waiting writer count).
*/
- public final int _wwc;
+ public int _wwc;
- private Stats(ICURWLock lock) {
- this(lock.stat_rc, lock.stat_mrc, lock.stat_wrc, lock.stat_wc, lock.stat_wwc);
+ private Stats() {
+ }
+
+ private Stats(Stats rhs) {
+ this(rhs._rc, rhs._mrc, rhs._wrc, rhs._wc, rhs._wwc);
}
private Stats(int rc, int mrc, int wrc, int wc, int wwc) {
@@ -87,19 +87,108 @@ public class ICURWLock {
}
/**
- * Reset the stats.
+ * Reset the stats. Returns existing stats, if any.
*/
- public void clearStats() {
- stat_rc = stat_mrc = stat_wrc = stat_wc = stat_wwc = 0;
+ public synchronized Stats resetStats() {
+ Stats result = stats;
+ stats = new Stats();
+ return result;
+ }
+
+ /**
+ * Clear the stats (stop collecting stats). Returns existing stats, if any.
+ */
+ public synchronized Stats clearStats() {
+ Stats result = stats;
+ stats = null;
+ return result;
}
/**
- * Return a snapshot of the current stats. This does not clear the stats.
+ * Return a snapshot of the current stats. This does not reset the stats.
*/
- public Stats getStats() {
- return new Stats(this);
+ public synchronized Stats getStats() {
+ return new Stats(stats);
}
+ // utilities
+
+ private synchronized boolean gotRead() {
+ ++rc;
+ if (stats != null) {
+ ++stats._rc;
+ if (rc > 1) ++stats._mrc;
+ }
+ return true;
+ }
+
+ private synchronized boolean getRead() {
+ if (rc >= 0 && wwc == 0) {
+ return gotRead();
+ }
+ ++wrc;
+ return false;
+ }
+
+ private synchronized boolean retryRead() {
+ if (stats != null) ++stats._wrc;
+ if (rc >= 0 && wwc == 0) {
+ --wrc;
+ return gotRead();
+ }
+ return false;
+ }
+
+ private synchronized boolean finishRead() {
+ if (rc > 0) {
+ return (0 == --rc && wwc > 0);
+ }
+ throw new InternalError("no current reader to release");
+ }
+
+ private synchronized boolean gotWrite() {
+ rc = -1;
+ if (stats != null) {
+ ++stats._wc;
+ }
+ return true;
+ }
+
+ private synchronized boolean getWrite() {
+ if (rc == 0) {
+ return gotWrite();
+ }
+ ++wwc;
+ return false;
+ }
+
+ private synchronized boolean retryWrite() {
+ if (stats != null) ++stats._wwc;
+ if (rc == 0) {
+ --wwc;
+ return gotWrite();
+ }
+ return false;
+ }
+
+ private static final int NOTIFY_NONE = 0;
+ private static final int NOTIFY_WRITERS = 1;
+ private static final int NOTIFY_READERS = 2;
+
+ private synchronized int finishWrite() {
+ if (rc < 0) {
+ rc = 0;
+ if (wwc > 0) {
+ return NOTIFY_WRITERS;
+ } else if (wrc > 0) {
+ return NOTIFY_READERS;
+ } else {
+ return NOTIFY_NONE;
+ }
+ }
+ throw new InternalError("no current writer to release");
+ }
+
/**
* Acquire a read lock, blocking until a read lock is
* available. Multiple readers can concurrently hold the read
@@ -110,18 +199,19 @@ public class ICURWLock {
* increment the active reader count and return. Caller must call
* releaseRead when done (for example, in a finally block).
*/
- public synchronized void acquireRead() {
- if (rc >= 0 && wwc == 0) {
- ++rc;
- ++stat_rc;
- if (rc > 1) ++stat_mrc;
- } else {
- ++wrc;
- ++stat_wrc;
- try {
- wait();
- }
- catch (InterruptedException e) {
+ public void acquireRead() {
+ if (!getRead()) {
+ for (;;) {
+ try {
+ synchronized (readLock) {
+ readLock.wait();
+ }
+ if (retryRead()) {
+ return;
+ }
+ }
+ catch (InterruptedException e) {
+ }
}
}
}
@@ -134,13 +224,11 @@ public class ICURWLock {
* waiting writer. Call when finished with work
* controlled by acquireRead.
*/
- public synchronized void releaseRead() {
- if (rc > 0) {
- if (0 == --rc) {
- notifyWaitingWriter();
+ public void releaseRead() {
+ if (finishRead()) {
+ synchronized (writeLock) {
+ writeLock.notify();
}
- } else {
- throw new InternalError("no current reader to release");
}
}
@@ -156,49 +244,18 @@ public class ICURWLock {
* block).
*/
public void acquireWrite() {
- // Do a quick check up top, to save us the lock allocation and
- // extra synch in the case where there is no contention for
- // writing. This is common in ICUService.
-
- synchronized (this) {
- if (rc == 0 && wwc == 0) {
- rc = -1;
- ++stat_wc;
- return;
- }
- }
-
- // We assume at this point that there is an active reader or a
- // waiting writer, so we don't recheck, though we could.
-
- // Create a lock for this thread only, it will be the only
- // thread notified when the lock comes to the front of the
- // waiting writer list. We synchronize on the lock first, and
- // then this, because when we release the lock on this, the
- // lock will be available to the world. If another thread
- // removed and notified that lock before we synchronized and
- // waited on it, we'd miss the only notification, and we would
- // wait on it forever. So we synchronized on it before we
- // make it available, so that we're guaranteed to be waiting on
- // it before any notification can occur.
-
- Object lock = new Object();
- synchronized (lock) {
- synchronized (this) {
- // again, we've assumed we don't have multiple waiting
- // writers, so we leave this null until we need it.
- // Once created, we keep it around.
- if (ww == null) {
- ww = new LinkedList();
+ if (!getWrite()) {
+ for (;;) {
+ try {
+ synchronized (writeLock) {
+ writeLock.wait();
+ }
+ if (retryWrite()) {
+ return;
+ }
+ }
+ catch (InterruptedException e) {
}
- ww.addLast(lock);
- ++wwc;
- ++stat_wwc;
- }
- try {
- lock.wait();
- }
- catch (InterruptedException e) {
}
}
}
@@ -212,43 +269,20 @@ public class ICURWLock {
* writer, if any. Call when finished with work controlled by
* acquireWrite.
*/
- public synchronized void releaseWrite() {
- if (rc < 0) {
- if (wrc > 0) {
- rc = wrc;
- wrc = 0;
- if (rc > 0) {
- stat_rc += rc;
- if (rc > 1) {
- stat_mrc += rc - 1;
- }
- }
- notifyAll();
- } else {
- rc = 0;
- notifyWaitingWriter();
+ public void releaseWrite() {
+ switch (finishWrite()) {
+ case NOTIFY_WRITERS:
+ synchronized (writeLock) {
+ writeLock.notify();
}
- } else {
- throw new InternalError("no current writer to release");
- }
- }
-
- /**
- * If there is a waiting writer thread, mark us as active for
- * writing, remove it from the list, and notify it.
- */
- private void notifyWaitingWriter() {
- // only called within a block synchronized on this
- // we don't assume there is necessarily a waiting writer,
- // no no error if there isn't.
- if (wwc > 0) {
- rc = -1;
- Object lock = ww.removeFirst();
- --wwc;
- ++stat_wc;
- synchronized (lock) {
- lock.notify();
+ break;
+ case NOTIFY_READERS:
+ synchronized (readLock) {
+ readLock.notifyAll();
}
+ break;
+ case NOTIFY_NONE:
+ break;
}
}
}
diff --git a/icu4j/src/com/ibm/icu/impl/ICUService.java b/icu4j/src/com/ibm/icu/impl/ICUService.java
index 22271a57d1..a7bb24eb2b 100644
--- a/icu4j/src/com/ibm/icu/impl/ICUService.java
+++ b/icu4j/src/com/ibm/icu/impl/ICUService.java
@@ -641,9 +641,11 @@ public class ICUService extends ICUNotifier {
* This also resets the statistics. Used for debugging purposes.
*/
public String stats() {
- String stats = factoryLock.getStats().toString();
- factoryLock.clearStats();
- return stats;
+ ICURWLock.Stats stats = factoryLock.clearStats();
+ if (stats != null) {
+ return stats.toString();
+ }
+ return "no stats";
}
}