7107611: sun.security.pkcs11.SessionManager is scalability blocker
authorascarpino
Wed, 19 Mar 2014 11:48:06 -0700
changeset 23552 46ede2de91cb
parent 23551 28cf58aa3f36
child 23553 0d1adf0afc20
7107611: sun.security.pkcs11.SessionManager is scalability blocker Reviewed-by: valeriep
jdk/src/share/classes/sun/security/pkcs11/SessionManager.java
--- a/jdk/src/share/classes/sun/security/pkcs11/SessionManager.java	Wed Mar 19 19:05:13 2014 +0100
+++ b/jdk/src/share/classes/sun/security/pkcs11/SessionManager.java	Wed Mar 19 11:48:06 2014 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -34,6 +34,9 @@
 import sun.security.pkcs11.wrapper.*;
 import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
 
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.atomic.AtomicInteger;
+
 /**
  * Session manager. There is one session manager object per PKCS#11
  * provider. It allows code to checkout a session, release it
@@ -77,7 +80,7 @@
     private final int maxSessions;
 
     // total number of active sessions
-    private int activeSessions;
+    private AtomicInteger activeSessions = new AtomicInteger();
 
     // pool of available object sessions
     private final Pool objSessions;
@@ -118,7 +121,7 @@
         return (maxSessions <= DEFAULT_MAX_SESSIONS);
     }
 
-    synchronized Session getObjSession() throws PKCS11Exception {
+    Session getObjSession() throws PKCS11Exception {
         Session session = objSessions.poll();
         if (session != null) {
             return ensureValid(session);
@@ -131,7 +134,7 @@
         return ensureValid(session);
     }
 
-    synchronized Session getOpSession() throws PKCS11Exception {
+    Session getOpSession() throws PKCS11Exception {
         Session session = opSessions.poll();
         if (session != null) {
             return ensureValid(session);
@@ -139,7 +142,7 @@
         // create a new session rather than re-using an obj session
         // that avoids potential expensive cancels() for Signatures & RSACipher
         if (maxSessions == Integer.MAX_VALUE ||
-                activeSessions < maxSessions) {
+                activeSessions.get() < maxSessions) {
             session = openSession();
             return ensureValid(session);
         }
@@ -155,20 +158,20 @@
         return session;
     }
 
-    synchronized Session killSession(Session session) {
+    Session killSession(Session session) {
         if ((session == null) || (token.isValid() == false)) {
             return null;
         }
         if (debug != null) {
             String location = new Exception().getStackTrace()[2].toString();
             System.out.println("Killing session (" + location + ") active: "
-                + activeSessions);
+                + activeSessions.get());
         }
         closeSession(session);
         return null;
     }
 
-    synchronized Session releaseSession(Session session) {
+    Session releaseSession(Session session) {
         if ((session == null) || (token.isValid() == false)) {
             return null;
         }
@@ -181,13 +184,13 @@
         return null;
     }
 
-    synchronized void demoteObjSession(Session session) {
+    void demoteObjSession(Session session) {
         if (token.isValid() == false) {
             return;
         }
         if (debug != null) {
             System.out.println("Demoting session, active: " +
-                activeSessions);
+                activeSessions.get());
         }
         boolean present = objSessions.remove(session);
         if (present == false) {
@@ -200,18 +203,21 @@
 
     private Session openSession() throws PKCS11Exception {
         if ((maxSessions != Integer.MAX_VALUE) &&
-                (activeSessions >= maxSessions)) {
+                (activeSessions.get() >= maxSessions)) {
             throw new ProviderException("No more sessions available");
         }
+
         long id = token.p11.C_OpenSession
                     (token.provider.slotID, openSessionFlags, null, null);
         Session session = new Session(token, id);
-        activeSessions++;
+        activeSessions.incrementAndGet();
         if (debug != null) {
-            if (activeSessions > maxActiveSessions) {
-                maxActiveSessions = activeSessions;
-                if (maxActiveSessions % 10 == 0) {
-                    System.out.println("Open sessions: " + maxActiveSessions);
+            synchronized(this) {
+                if (activeSessions.get() > maxActiveSessions) {
+                    maxActiveSessions = activeSessions.get();
+                    if (maxActiveSessions % 10 == 0) {
+                        System.out.println("Open sessions: " + maxActiveSessions);
+                    }
                 }
             }
         }
@@ -220,18 +226,18 @@
 
     private void closeSession(Session session) {
         session.close();
-        activeSessions--;
+        activeSessions.decrementAndGet();
     }
 
-    private static final class Pool {
+    public static final class Pool {
 
         private final SessionManager mgr;
 
-        private final List<Session> pool;
+        private final ConcurrentLinkedDeque<Session> pool;
 
         Pool(SessionManager mgr) {
-            this.mgr = mgr;
-            pool = new ArrayList<Session>();
+           this.mgr = mgr;
+           pool = new ConcurrentLinkedDeque<Session>();
         }
 
         boolean remove(Session session) {
@@ -239,45 +245,40 @@
         }
 
         Session poll() {
-            int n = pool.size();
-            if (n == 0) {
-                return null;
-            }
-            Session session = pool.remove(n - 1);
-            return session;
+            return pool.pollLast();
         }
 
         void release(Session session) {
-            pool.add(session);
-            // if there are idle sessions, close them
+            pool.offer(session);
             if (session.hasObjects()) {
                 return;
             }
+
             int n = pool.size();
             if (n < 5) {
                 return;
             }
-            Session oldestSession = pool.get(0);
+
+            Session oldestSession;
             long time = System.currentTimeMillis();
-            if (session.isLive(time) && oldestSession.isLive(time)) {
-                return;
-            }
-            Collections.sort(pool);
             int i = 0;
-            while (i < n - 1) { // always keep at least 1 session open
-                oldestSession = pool.get(i);
-                if (oldestSession.isLive(time)) {
+            // Check if the session head is too old and continue through queue
+            // until only one is left.
+            do {
+                oldestSession = pool.peek();
+                if (oldestSession == null || oldestSession.isLive(time) ||
+                        !pool.remove(oldestSession)) {
                     break;
                 }
+
                 i++;
                 mgr.closeSession(oldestSession);
-            }
+            } while ((n - i) > 1);
+
             if (debug != null) {
                 System.out.println("Closing " + i + " idle sessions, active: "
                         + mgr.activeSessions);
             }
-            List<Session> subList = pool.subList(0, i);
-            subList.clear();
         }
 
     }