6941130: Semaphore should throw if number of permits overflows or underflows
authormartin
Wed, 07 Apr 2010 12:30:49 -0700
changeset 5202 20198b5f3653
parent 5201 37e71048195f
child 5203 2b0ae695ba92
child 5288 e91651d24b2a
child 5291 d6df082f6524
6941130: Semaphore should throw if number of permits overflows or underflows Summary: Check if release could make number of permits negative Reviewed-by: dl, dholmes
jdk/src/share/classes/java/util/concurrent/Semaphore.java
jdk/test/java/util/concurrent/Semaphore/PermitOverflow.java
--- a/jdk/src/share/classes/java/util/concurrent/Semaphore.java	Tue Apr 06 15:45:21 2010 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/Semaphore.java	Wed Apr 07 12:30:49 2010 -0700
@@ -191,8 +191,11 @@
 
         protected final boolean tryReleaseShared(int releases) {
             for (;;) {
-                int p = getState();
-                if (compareAndSetState(p, p + releases))
+                int current = getState();
+                int next = current + releases;
+                if (next < current) // overflow
+                    throw new Error("Maximum permit count exceeded");
+                if (compareAndSetState(current, next))
                     return true;
             }
         }
@@ -201,6 +204,8 @@
             for (;;) {
                 int current = getState();
                 int next = current - reductions;
+                if (next > current) // underflow
+                    throw new Error("Permit count underflow");
                 if (compareAndSetState(current, next))
                     return;
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/concurrent/Semaphore/PermitOverflow.java	Wed Apr 07 12:30:49 2010 -0700
@@ -0,0 +1,102 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
+ */
+
+/*
+ * @test
+ * @bug 6941130
+ * @summary Numeric overflow/underflow of permits causes Error throw
+ */
+
+import java.util.concurrent.Semaphore;
+
+public class PermitOverflow {
+
+    public static void main(String[] args) throws Throwable {
+        for (boolean fair : new boolean[] { true, false }) {
+            Semaphore sem = new Semaphore(Integer.MAX_VALUE - 1, fair);
+            if (sem.availablePermits() != Integer.MAX_VALUE - 1)
+                throw new RuntimeException();
+            try {
+                sem.release(2);
+                throw new RuntimeException();
+            } catch (Error expected) {
+            }
+            sem.release(1);
+            if (sem.availablePermits() != Integer.MAX_VALUE)
+                throw new RuntimeException();
+            try {
+                sem.release(1);
+                throw new RuntimeException();
+            } catch (Error expected) {
+            }
+            try {
+                sem.release(Integer.MAX_VALUE);
+                throw new RuntimeException();
+            } catch (Error expected) {
+            }
+        }
+
+        class Sem extends Semaphore {
+            public Sem(int permits, boolean fair) {
+                super(permits, fair);
+            }
+            public void reducePermits(int reduction) {
+                super.reducePermits(reduction);
+            }
+        }
+
+        for (boolean fair : new boolean[] { true, false }) {
+            Sem sem = new Sem(Integer.MIN_VALUE + 1, fair);
+            if (sem.availablePermits() != Integer.MIN_VALUE + 1)
+                throw new RuntimeException();
+            try {
+                sem.reducePermits(2);
+                throw new RuntimeException();
+            } catch (Error expected) {
+            }
+            sem.reducePermits(1);
+            if (sem.availablePermits() != Integer.MIN_VALUE)
+                throw new RuntimeException();
+            try {
+                sem.reducePermits(1);
+                throw new RuntimeException();
+            } catch (Error expected) {
+            }
+            try {
+                sem.reducePermits(Integer.MAX_VALUE);
+                throw new RuntimeException();
+            } catch (Error expected) {
+            }
+        }
+    }
+}