--- old/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Key.java 2016-03-01 13:57:23.636127178 -0800 +++ new/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Key.java 2016-03-01 13:57:23.496124674 -0800 @@ -45,6 +45,7 @@ import sun.security.pkcs11.wrapper.*; import static sun.security.pkcs11.wrapper.PKCS11Constants.*; +import sun.security.util.Debug; import sun.security.util.DerValue; import sun.security.util.Length; import sun.security.util.ECUtil; @@ -1110,10 +1111,27 @@ } private static void drainRefQueueBounded() { + Session session = null; + Token token = null; while (true) { SessionKeyRef next = (SessionKeyRef) refQueue.poll(); - if (next == null) break; - next.dispose(); + if (next == null) { + break; + } + try { + // If this key's token is the same as the previous key, the + // same session can be used for C_DestroyObject. + if (next.session.token != token) { + // If we have a previous token and session, release the session + if (token != null && session != null) + token.releaseSession(session); + token = next.session.token; + session = token.getOpSession(); + } + next.dispose(session); + } catch (PKCS11Exception e) { + // ignore + } } } @@ -1127,25 +1145,24 @@ this.session = session; this.session.addObject(); refList.add(this); - // TBD: run at some interval and not every time? drainRefQueueBounded(); } + private void dispose(Session s) { + try { + session.token.p11.C_DestroyObject(s.id(), keyID); + } catch (PKCS11Exception e) { + // ignore + } finally { + dispose(); + } + + } + private void dispose() { refList.remove(this); - if (session.token.isValid()) { - Session newSession = null; - try { - newSession = session.token.getOpSession(); - session.token.p11.C_DestroyObject(newSession.id(), keyID); - } catch (PKCS11Exception e) { - // ignore - } finally { - this.clear(); - session.token.releaseSession(newSession); - session.removeObject(); - } - } + this.clear(); + session.removeObject(); } public int compareTo(SessionKeyRef other) { --- old/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/SessionManager.java 2016-03-01 13:57:24.044134477 -0800 +++ new/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/SessionManager.java 2016-03-01 13:57:23.900131901 -0800 @@ -34,7 +34,7 @@ import sun.security.pkcs11.wrapper.*; import static sun.security.pkcs11.wrapper.PKCS11Constants.*; -import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicInteger; /** @@ -112,8 +112,8 @@ } maxSessions = (int)Math.min(n, Integer.MAX_VALUE); this.token = token; - this.objSessions = new Pool(this); - this.opSessions = new Pool(this); + this.objSessions = new Pool(this, true); + this.opSessions = new Pool(this, false); if (debug != null) { maxActiveSessionsLock = new Object(); } @@ -236,12 +236,18 @@ public static final class Pool { private final SessionManager mgr; + private final AbstractQueue pool; + private final int SESSION_MAX = 5; - private final ConcurrentLinkedDeque pool; - - Pool(SessionManager mgr) { - this.mgr = mgr; - pool = new ConcurrentLinkedDeque(); + // Object session pools can contain unlimited sessions. + // Operation session pools are limited and enforced be the queue. + Pool(SessionManager mgr, boolean obj) { + this.mgr = mgr; + if (obj) { + pool = new LinkedBlockingQueue(); + } else { + pool = new LinkedBlockingQueue(SESSION_MAX); + } } boolean remove(Session session) { @@ -249,24 +255,24 @@ } Session poll() { - return pool.pollLast(); + return pool.poll(); } void release(Session session) { - pool.offer(session); - if (session.hasObjects()) { - return; - } - - int n = pool.size(); - if (n < 5) { - return; + // Object session pools never return false, only Operation ones + if (!pool.offer(session)) { + mgr.closeSession(session); + free(); } + } + // Free any old operation session if this queue is full + void free() { + int n = SESSION_MAX - 1; + int i = 0; Session oldestSession; long time = System.currentTimeMillis(); - int i = 0; - // Check if the session head is too old and continue through queue + // Check if the session head is too old and continue through pool // until only one is left. do { oldestSession = pool.peek();