7107611: sun.security.pkcs11.SessionManager is scalability blocker
Reviewed-by: valeriep
--- 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();
}
}