8277954: Replace use of monitors with explicit locks in the JDK LDAP provider implementation

Reviewed-by: dfuchs
This commit is contained in:
Aleksei Efimov 2023-09-11 14:05:48 +00:00
parent 4cb4637b79
commit 66b6a5a84f
11 changed files with 872 additions and 579 deletions

View File

@ -51,6 +51,8 @@ import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import javax.net.SocketFactory;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.HandshakeCompletedEvent;
@ -174,7 +176,10 @@ public final class Connection implements Runnable {
private volatile boolean isUpgradedToStartTls;
// Lock to maintain isUpgradedToStartTls state
final Object startTlsLock = new Object();
final ReentrantLock startTlsLock = new ReentrantLock();
// Connection instance lock
private final ReentrantLock lock = new ReentrantLock();
private static final boolean IS_HOSTNAME_VERIFICATION_DISABLED
= hostnameVerificationDisabledValue();
@ -373,8 +378,13 @@ public final class Connection implements Runnable {
//
////////////////////////////////////////////////////////////////////////////
synchronized int getMsgId() {
int getMsgId() {
lock.lock();
try {
return ++outMsgId;
} finally {
lock.unlock();
}
}
LdapRequest writeRequest(BerEncoder ber, int msgId) throws IOException {
@ -408,9 +418,12 @@ public final class Connection implements Runnable {
}
try {
synchronized (this) {
lock.lock();
try {
outStream.write(ber.getBuf(), 0, ber.getDataLen());
outStream.flush();
} finally {
lock.unlock();
}
} catch (IOException e) {
cleanup(null, true);
@ -427,11 +440,14 @@ public final class Connection implements Runnable {
BerDecoder rber;
// If socket closed, don't even try
synchronized (this) {
lock.lock();
try {
if (sock == null) {
throw new ServiceUnavailableException(host + ":" + port +
"; socket closed");
}
} finally {
lock.unlock();
}
IOException ioException = null;
@ -476,8 +492,9 @@ public final class Connection implements Runnable {
//
////////////////////////////////////////////////////////////////////////////
private synchronized void addRequest(LdapRequest ldapRequest) {
private void addRequest(LdapRequest ldapRequest) {
lock.lock();
try {
LdapRequest ldr = pendingRequests;
if (ldr == null) {
pendingRequests = ldapRequest;
@ -486,10 +503,14 @@ public final class Connection implements Runnable {
ldapRequest.next = pendingRequests;
pendingRequests = ldapRequest;
}
} finally {
lock.unlock();
}
}
synchronized LdapRequest findRequest(int msgId) {
LdapRequest findRequest(int msgId) {
lock.lock();
try {
LdapRequest ldr = pendingRequests;
while (ldr != null) {
if (ldr.msgId == msgId) {
@ -498,10 +519,15 @@ public final class Connection implements Runnable {
ldr = ldr.next;
}
return null;
} finally {
lock.unlock();
}
}
synchronized void removeRequest(LdapRequest req) {
void removeRequest(LdapRequest req) {
lock.lock();
try {
LdapRequest ldr = pendingRequests;
LdapRequest ldrprev = null;
@ -519,6 +545,9 @@ public final class Connection implements Runnable {
ldrprev = ldr;
ldr = ldr.next;
}
} finally {
lock.unlock();
}
}
void abandonRequest(LdapRequest ldr, Control[] reqCtls) {
@ -546,9 +575,12 @@ public final class Connection implements Runnable {
ber.getDataLen());
}
synchronized (this) {
lock.lock();
try {
outStream.write(ber.getBuf(), 0, ber.getDataLen());
outStream.flush();
} finally {
lock.unlock();
}
} catch (IOException ex) {
@ -558,13 +590,18 @@ public final class Connection implements Runnable {
// Don't expect any response for the abandon request.
}
synchronized void abandonOutstandingReqs(Control[] reqCtls) {
void abandonOutstandingReqs(Control[] reqCtls) {
lock.lock();
try {
LdapRequest ldr = pendingRequests;
while (ldr != null) {
abandonRequest(ldr, reqCtls);
pendingRequests = ldr = ldr.next;
}
} finally {
lock.unlock();
}
}
////////////////////////////////////////////////////////////////////////////
@ -601,9 +638,12 @@ public final class Connection implements Runnable {
0, ber.getDataLen());
}
synchronized (this) {
lock.lock();
try {
outStream.write(ber.getBuf(), 0, ber.getDataLen());
outStream.flush();
} finally {
lock.unlock();
}
} catch (IOException ex) {
@ -625,8 +665,8 @@ public final class Connection implements Runnable {
*/
void cleanup(Control[] reqCtls, boolean notifyParent) {
boolean nparent = false;
synchronized (this) {
lock.lock();
try {
useable = false;
if (sock != null) {
@ -674,6 +714,8 @@ public final class Connection implements Runnable {
ldr = ldr.next;
}
}
} finally {
lock.unlock();
}
if (nparent) {
parent.processConnectionClosure();
@ -723,7 +765,9 @@ public final class Connection implements Runnable {
// "synchronize" might lead to deadlock so don't synchronize method
// Use streamLock instead for synchronizing update to stream
public synchronized void replaceStreams(InputStream newIn, OutputStream newOut) {
public void replaceStreams(InputStream newIn, OutputStream newOut) {
lock.lock();
try {
if (debug) {
System.err.println("Replacing " + inStream + " with: " + newIn);
System.err.println("Replacing " + outStream + " with: " + newOut);
@ -741,15 +785,26 @@ public final class Connection implements Runnable {
// Replace stream
outStream = newOut;
} finally {
lock.unlock();
}
}
/*
* Replace streams and set isUpdradedToStartTls flag to the provided value
*/
public synchronized void replaceStreams(InputStream newIn, OutputStream newOut, boolean isStartTls) {
synchronized (startTlsLock) {
public void replaceStreams(InputStream newIn, OutputStream newOut, boolean isStartTls) {
lock.lock();
try {
startTlsLock.lock();
try {
replaceStreams(newIn, newOut);
isUpgradedToStartTls = isStartTls;
} finally {
startTlsLock.unlock();
}
} finally {
lock.unlock();
}
}
@ -765,8 +820,13 @@ public final class Connection implements Runnable {
* This ensures that there is no contention between the main thread
* and the Connection thread when the main thread updates inStream.
*/
private synchronized InputStream getInputStream() {
private InputStream getInputStream() {
lock.lock();
try {
return inStream;
} finally {
lock.unlock();
}
}
@ -817,31 +877,37 @@ public final class Connection implements Runnable {
* the safest thing to do is to shut it down.
*/
private final Object pauseLock = new Object(); // lock for reader to wait on while paused
// lock for reader to wait on while paused
private final ReentrantLock pauseLock = new ReentrantLock();
private final Condition pauseCondition = pauseLock.newCondition();
private boolean paused = false; // paused state of reader
/*
* Unpauses reader thread if it was paused
*/
private void unpauseReader() throws IOException {
synchronized (pauseLock) {
pauseLock.lock();
try {
if (paused) {
if (debug) {
System.err.println("Unpausing reader; read from: " +
inStream);
}
paused = false;
pauseLock.notify();
pauseCondition.signal();
}
} finally {
pauseLock.unlock();
}
}
/*
* Pauses reader so that it stops reading from the input stream.
* Reader blocks on pauseLock instead of read().
* MUST be called from within synchronized (pauseLock) clause.
* MUST be called with pauseLock locked.
*/
private void pauseReader() throws IOException {
assert pauseLock.isHeldByCurrentThread();
if (debug) {
System.err.println("Pausing reader; was reading from: " +
inStream);
@ -849,7 +915,7 @@ public final class Connection implements Runnable {
paused = true;
try {
while (paused) {
pauseLock.wait(); // notified by unpauseReader
pauseCondition.await(); // notified by unpauseReader
}
} catch (InterruptedException e) {
throw new InterruptedIOException(
@ -985,7 +1051,8 @@ public final class Connection implements Runnable {
* to ensure that reader goes into paused state
* before writer can attempt to unpause reader
*/
synchronized (pauseLock) {
pauseLock.lock();
try {
needPause = ldr.addReplyBer(retBer);
if (needPause) {
/*
@ -996,6 +1063,8 @@ public final class Connection implements Runnable {
}
// else release pauseLock
} finally {
pauseLock.unlock();
}
} else {
// System.err.println("Cannot find" +
@ -1077,12 +1146,17 @@ public final class Connection implements Runnable {
*/
private volatile HandshakeListener tlsHandshakeListener;
public synchronized void setHandshakeCompletedListener(SSLSocket sslSocket) {
public void setHandshakeCompletedListener(SSLSocket sslSocket) {
lock.lock();
try {
if (tlsHandshakeListener != null)
tlsHandshakeListener.tlsHandshakeCompleted.cancel(false);
tlsHandshakeListener = new HandshakeListener();
sslSocket.addHandshakeCompletedListener(tlsHandshakeListener);
} finally {
lock.unlock();
}
}
public X509Certificate getTlsServerCertificate()

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2023, 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
@ -27,6 +27,8 @@ package com.sun.jndi.ldap;
import java.util.Vector;
import java.util.EventObject;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import javax.naming.event.NamingEvent;
import javax.naming.event.NamingExceptionEvent;
@ -47,6 +49,10 @@ import javax.naming.ldap.UnsolicitedNotificationListener;
final class EventQueue implements Runnable {
private static final boolean debug = false;
// EventQueue instance lock
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private static class QueueElement {
QueueElement next = null;
QueueElement prev = null;
@ -86,7 +92,9 @@ final class EventQueue implements Runnable {
* are notified.
* @param vector List of NamingListeners that will be notified of event.
*/
synchronized void enqueue(EventObject event, Vector<NamingListener> vector) {
void enqueue(EventObject event, Vector<NamingListener> vector) {
lock.lock();
try {
QueueElement newElt = new QueueElement(event, vector);
if (head == null) {
@ -97,7 +105,10 @@ final class EventQueue implements Runnable {
head.prev = newElt;
head = newElt;
}
notify();
condition.signal();
} finally {
lock.unlock();
}
}
/**
@ -108,10 +119,11 @@ final class EventQueue implements Runnable {
* @exception java.lang.InterruptedException if any thread has
* interrupted this thread.
*/
private synchronized QueueElement dequeue()
throws InterruptedException {
private QueueElement dequeue() throws InterruptedException {
lock.lock();
try {
while (tail == null)
wait();
condition.await();
QueueElement elt = tail;
tail = elt.prev;
if (tail == null) {
@ -121,6 +133,9 @@ final class EventQueue implements Runnable {
}
elt.prev = elt.next = null;
return elt;
} finally {
lock.unlock();
}
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2023, 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
@ -30,6 +30,7 @@ import java.util.Vector;
import java.util.EventObject;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import javax.naming.*;
import javax.naming.event.*;
@ -117,6 +118,9 @@ final class EventSupport {
private LdapCtx ctx;
// EventSupport instance lock. Accessed by LdapCtx
final ReentrantLock lock = new ReentrantLock();
/**
* NamingEventNotifiers; hashed by search arguments;
*/
@ -149,9 +153,11 @@ final class EventSupport {
* 2. ensure that NamingEventNotifier thread's access to 'notifiers'
* is safe
*/
synchronized void addNamingListener(String nm, int scope,
void addNamingListener(String nm, int scope,
NamingListener l) throws NamingException {
lock.lock();
try {
if (l instanceof ObjectChangeListener ||
l instanceof NamespaceChangeListener) {
NotifierArgs args = new NotifierArgs(nm, scope, l);
@ -170,7 +176,10 @@ final class EventSupport {
unsolicited = new Vector<>(3);
}
unsolicited.addElement((UnsolicitedNotificationListener)l);
unsolicited.addElement((UnsolicitedNotificationListener) l);
}
} finally {
lock.unlock();
}
}
@ -178,9 +187,11 @@ final class EventSupport {
* Adds {@code l} to list of listeners interested in {@code nm}
* and filter.
*/
synchronized void addNamingListener(String nm, String filter,
void addNamingListener(String nm, String filter,
SearchControls ctls, NamingListener l) throws NamingException {
lock.lock();
try {
if (l instanceof ObjectChangeListener ||
l instanceof NamespaceChangeListener) {
NotifierArgs args = new NotifierArgs(nm, filter, ctls, l);
@ -198,14 +209,19 @@ final class EventSupport {
if (unsolicited == null) {
unsolicited = new Vector<>(3);
}
unsolicited.addElement((UnsolicitedNotificationListener)l);
unsolicited.addElement((UnsolicitedNotificationListener) l);
}
} finally {
lock.unlock();
}
}
/**
* Removes {@code l} from all notifiers in this context.
*/
synchronized void removeNamingListener(NamingListener l) {
void removeNamingListener(NamingListener l) {
lock.lock();
try {
if (debug) {
System.err.println("EventSupport removing listener");
}
@ -235,10 +251,18 @@ final class EventSupport {
if (unsolicited != null) {
unsolicited.removeElement(l);
}
} finally {
lock.unlock();
}
}
synchronized boolean hasUnsolicited() {
boolean hasUnsolicited() {
lock.lock();
try {
return (unsolicited != null && unsolicited.size() > 0);
} finally {
lock.unlock();
}
}
/**
@ -246,7 +270,9 @@ final class EventSupport {
* Called by NamingEventNotifier to remove itself when it encounters
* a NamingException.
*/
synchronized void removeDeadNotifier(NotifierArgs info) {
void removeDeadNotifier(NotifierArgs info) {
lock.lock();
try {
if (debug) {
System.err.println("EventSupport.removeDeadNotifier: " + info.name);
}
@ -256,6 +282,9 @@ final class EventSupport {
// null in cleanup()
notifiers.remove(info);
}
} finally {
lock.unlock();
}
}
/**
@ -263,7 +292,9 @@ final class EventSupport {
* package private;
* Called by LdapCtx when its clnt receives an unsolicited notification.
*/
synchronized void fireUnsolicited(Object obj) {
void fireUnsolicited(Object obj) {
lock.lock();
try {
if (debug) {
System.err.println("EventSupport.fireUnsolicited: " + obj + " "
+ unsolicited);
@ -280,7 +311,7 @@ final class EventSupport {
// Fire UnsolicitedNotification to unsolicited listeners
UnsolicitedNotificationEvent evt =
new UnsolicitedNotificationEvent(ctx, (UnsolicitedNotification)obj);
new UnsolicitedNotificationEvent(ctx, (UnsolicitedNotification) obj);
queueEvent(evt, unsolicited);
} else if (obj instanceof NamingException) {
@ -288,7 +319,7 @@ final class EventSupport {
// Fire NamingExceptionEvent to unsolicited listeners.
NamingExceptionEvent evt =
new NamingExceptionEvent(ctx, (NamingException)obj);
new NamingExceptionEvent(ctx, (NamingException) obj);
queueEvent(evt, unsolicited);
// When an exception occurs, the unsolicited listeners
@ -299,6 +330,9 @@ final class EventSupport {
unsolicited = null;
}
} finally {
lock.unlock();
}
}
/**
@ -306,7 +340,9 @@ final class EventSupport {
* stops the event queue from dispatching events.
* Package private; used by LdapCtx.
*/
synchronized void cleanup() {
void cleanup() {
lock.lock();
try {
if (debug) System.err.println("EventSupport clean up");
if (notifiers != null) {
for (NamingEventNotifier notifier : notifiers.values()) {
@ -319,6 +355,9 @@ final class EventSupport {
eventQueue = null;
}
// %%% Should we fire NamingExceptionEvents to unsolicited listeners?
} finally {
lock.unlock();
}
}
/*
@ -332,8 +371,10 @@ final class EventSupport {
* them to the registered listeners.
* Package private; used by NamingEventNotifier to fire events
*/
synchronized void queueEvent(EventObject event,
void queueEvent(EventObject event,
Vector<? extends NamingListener> vector) {
lock.lock();
try {
if (notifiers == null) {
// That means cleanup() already done, not queue event anymore,
// otherwise, new created EventQueue will not been cleanup.
@ -352,8 +393,11 @@ final class EventSupport {
*/
@SuppressWarnings("unchecked") // clone()
Vector<NamingListener> v =
(Vector<NamingListener>)vector.clone();
(Vector<NamingListener>) vector.clone();
eventQueue.enqueue(event, v);
} finally {
lock.unlock();
}
}
// No finalize() needed because EventSupport is always owned by

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2023, 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
@ -30,6 +30,7 @@ import java.util.ArrayList;
import java.util.Locale;
import java.util.Vector;
import java.util.Hashtable;
import java.util.concurrent.locks.ReentrantLock;
import javax.naming.*;
import javax.naming.directory.*;
@ -125,6 +126,8 @@ public final class LdapClient implements PooledConnection {
private final PoolCallback pcb;
private final boolean pooled;
private boolean authenticateCalled = false;
// LdapClient instance lock, accessed by LdapCtx
final ReentrantLock lock = new ReentrantLock();
////////////////////////////////////////////////////////////////////////////
//
@ -145,19 +148,23 @@ public final class LdapClient implements PooledConnection {
pooled = (pcb != null);
}
synchronized boolean authenticateCalled() {
boolean authenticateCalled() {
lock.lock();
try {
return authenticateCalled;
} finally {
lock.unlock();
}
}
synchronized LdapResult
authenticate(boolean initial, String name, Object pw, int version,
LdapResult authenticate(boolean initial, String name, Object pw, int version,
String authMechanism, Control[] ctls, Hashtable<?,?> env)
throws NamingException {
lock.lock();
try {
int readTimeout = conn.readTimeout;
conn.readTimeout = conn.connectTimeout;
LdapResult res = null;
LdapResult res;
try {
authenticateCalled = true;
@ -193,7 +200,7 @@ public final class LdapClient implements PooledConnection {
((ctls != null) && (ctls.length > 0))) {
try {
// anonymous bind; update name/pw for LDAPv2 retry
res = ldapBind(name=null, (byte[])(pw=null), ctls, null,
res = ldapBind(name = null, (byte[]) (pw = null), ctls, null,
false);
if (res.status == LdapClient.LDAP_SUCCESS) {
conn.setBound();
@ -303,6 +310,9 @@ public final class LdapClient implements PooledConnection {
} finally {
conn.readTimeout = readTimeout;
}
} finally {
lock.unlock();
}
}
/**
@ -313,10 +323,12 @@ public final class LdapClient implements PooledConnection {
* @param auth The authentication mechanism
*
*/
public synchronized LdapResult ldapBind(String dn, byte[]toServer,
public LdapResult ldapBind(String dn, byte[]toServer,
Control[] bindCtls, String auth, boolean pauseAfterReceipt)
throws java.io.IOException, NamingException {
throws IOException, NamingException {
lock.lock();
try {
ensureOpen();
// flush outstanding requests
@ -388,6 +400,9 @@ public final class LdapClient implements PooledConnection {
conn.removeRequest(req);
return res;
} finally {
lock.unlock();
}
}
/**
@ -406,12 +421,16 @@ public final class LdapClient implements PooledConnection {
return conn.isUpgradedToStartTls();
}
synchronized void incRefCount() {
void incRefCount() {
lock.lock();
try {
++referenceCount;
if (debug > 1) {
System.err.println("LdapClient.incRefCount: " + referenceCount + " " + this);
}
} finally {
lock.unlock();
}
}
/**
@ -434,7 +453,9 @@ public final class LdapClient implements PooledConnection {
}
}
synchronized void close(Control[] reqCtls, boolean hardClose) {
void close(Control[] reqCtls, boolean hardClose) {
lock.lock();
try {
--referenceCount;
if (debug > 1) {
@ -459,6 +480,9 @@ public final class LdapClient implements PooledConnection {
}
}
}
} finally {
lock.unlock();
}
}
// NOTE: Should NOT be synchronized otherwise won't be able to close
@ -487,8 +511,13 @@ public final class LdapClient implements PooledConnection {
/*
* Used by connection pooling to close physical connection.
*/
public synchronized void closeConnection() {
public void closeConnection() {
lock.lock();
try {
forceClose(false); // this is a pool callback so no need to clean pool
} finally {
lock.unlock();
}
}
/**
@ -1491,7 +1520,8 @@ public final class LdapClient implements PooledConnection {
// removeUnsolicited() is invoked to remove an LdapCtx from this client.
//
////////////////////////////////////////////////////////////////////////////
private Vector<LdapCtx> unsolicited = new Vector<>(3);
private final Vector<LdapCtx> unsolicited = new Vector<>(3);
private final ReentrantLock unsolicitedLock = new ReentrantLock();
void addUnsolicited(LdapCtx ctx) {
if (debug > 0) {
System.err.println("LdapClient.addUnsolicited" + ctx);
@ -1533,7 +1563,8 @@ public final class LdapClient implements PooledConnection {
LdapCtx first = null;
UnsolicitedNotification notice = null;
synchronized (unsolicited) {
unsolicitedLock.lock();
try {
if (unsolicited.size() > 0) {
first = unsolicited.elementAt(0);
@ -1551,6 +1582,8 @@ public final class LdapClient implements PooledConnection {
first.convertControls(res.resControls) :
null);
}
} finally {
unsolicitedLock.unlock();
}
if (notice != null) {
@ -1579,11 +1612,14 @@ public final class LdapClient implements PooledConnection {
private void notifyUnsolicited(Object e) {
ArrayList<LdapCtx> unsolicitedCopy;
synchronized (unsolicited) {
unsolicitedLock.lock();
try {
unsolicitedCopy = new ArrayList<>(unsolicited);
if (e instanceof NamingException) {
unsolicited.setSize(0); // no more listeners after exception
}
} finally {
unsolicitedLock.unlock();
}
for (int i = 0; i < unsolicitedCopy.size(); i++) {
unsolicitedCopy.get(i).fireUnsolicited(e);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2023, 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
@ -43,6 +43,7 @@ import java.util.Hashtable;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Enumeration;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@ -300,6 +301,7 @@ public final class LdapCtx extends ComponentDirContext
private EventSupport eventSupport; // Event support helper for this ctx
private boolean unsolicited = false; // if there unsolicited listeners
private boolean sharable = true; // can share connection with other ctx
private final ReentrantLock lock = new ReentrantLock(); // LdapCtx instance lock
// -------------- Constructors -----------------------------------
@ -2649,7 +2651,9 @@ public final class LdapCtx extends ComponentDirContext
}
}
public synchronized void close() throws NamingException {
public void close() throws NamingException {
lock.lock();
try {
if (debug) {
System.err.println("LdapCtx: close() called " + this);
(new Throwable()).printStackTrace();
@ -2669,6 +2673,9 @@ public final class LdapCtx extends ComponentDirContext
return;
}
closeConnection(SOFT_CLOSE);
} finally {
lock.unlock();
}
// %%%: RL: There is no need to set these to null, as they're just
// variables whose contents and references will automatically
@ -2785,13 +2792,17 @@ public final class LdapCtx extends ComponentDirContext
} else if (!sharable || startTLS) {
synchronized (clnt) {
ReentrantLock clientLock = clnt.lock;
clientLock.lock();
try {
if (!clnt.isLdapv3
|| clnt.referenceCount > 1
|| clnt.usingSaslStreams()
|| !clnt.conn.useable) {
closeConnection(SOFT_CLOSE);
}
} finally {
clientLock.unlock();
}
// reset the cache before a new connection is established
schemaTrees = new Hashtable<>(11, 0.75f);
@ -2891,10 +2902,14 @@ public final class LdapCtx extends ComponentDirContext
}
LdapResult answer;
synchronized (clnt.conn.startTlsLock) {
ReentrantLock startTlsLock = clnt.conn.startTlsLock;
startTlsLock.lock();
try {
ensureCanTransmitCredentials(authMechanism);
answer = clnt.authenticate(initial, user, passwd, ldapVersion,
authMechanism, bindCtls, envprops);
} finally {
startTlsLock.unlock();
}
respCtls = answer.resControls; // retrieve (bind) response controls
@ -2967,12 +2982,19 @@ public final class LdapCtx extends ComponentDirContext
private int enumCount = 0;
private boolean closeRequested = false;
synchronized void incEnumCount() {
void incEnumCount() {
lock.lock();
try {
++enumCount;
if (debug) System.err.println("LdapCtx: " + this + " enum inc: " + enumCount);
} finally {
lock.unlock();
}
}
synchronized void decEnumCount() {
void decEnumCount() {
lock.lock();
try {
--enumCount;
if (debug) System.err.println("LdapCtx: " + this + " enum dec: " + enumCount);
@ -2983,6 +3005,9 @@ public final class LdapCtx extends ComponentDirContext
// ignore failures
}
}
} finally {
lock.unlock();
}
}
@ -3576,10 +3601,13 @@ public final class LdapCtx extends ComponentDirContext
}
}
public void addNamingListener(String nm, String filter, SearchControls ctls,
NamingListener l) throws NamingException {
public void addNamingListener(String nm, String filter,
SearchControls ctls, NamingListener l)
throws NamingException {
if (eventSupport == null)
eventSupport = new EventSupport(this);
eventSupport.addNamingListener(getTargetName(new CompositeName(nm)),
filter, cloneSearchControls(ctls), l);
@ -3653,9 +3681,13 @@ public final class LdapCtx extends ComponentDirContext
// addNamingListener must have created EventSupport already
ensureOpen();
synchronized (eventSupport) {
ReentrantLock eventSupportLock = eventSupport.lock;
eventSupportLock.lock();
try {
clnt.addUnsolicited(this);
unsolicited = true;
} finally {
eventSupportLock.unlock();
}
}
@ -3682,11 +3714,15 @@ public final class LdapCtx extends ComponentDirContext
}
// addNamingListener must have created EventSupport already
synchronized(eventSupport) {
ReentrantLock eventSupportLock = eventSupport.lock;
eventSupportLock.lock();
try {
if (unsolicited && clnt != null) {
clnt.removeUnsolicited(this);
}
unsolicited = false;
} finally {
eventSupportLock.unlock();
}
}
@ -3699,7 +3735,9 @@ public final class LdapCtx extends ComponentDirContext
System.out.println("LdapCtx.fireUnsolicited: " + obj);
}
// addNamingListener must have created EventSupport already
synchronized(eventSupport) {
ReentrantLock eventSupportLock = eventSupport.lock;
eventSupportLock.lock();
try {
if (unsolicited) {
eventSupport.fireUnsolicited(obj);
@ -3710,6 +3748,8 @@ public final class LdapCtx extends ComponentDirContext
// unsol listeners and it will handle its own cleanup
}
}
} finally {
eventSupportLock.unlock();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2023, 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
@ -28,6 +28,7 @@ package com.sun.jndi.ldap;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import javax.naming.NamingException;
import javax.naming.ldap.spi.LdapDnsProvider;
import javax.naming.ldap.spi.LdapDnsProviderResult;
@ -43,7 +44,7 @@ import sun.security.util.SecurityConstants;
final class LdapDnsProviderService {
private static volatile LdapDnsProviderService service;
private static final Object LOCK = new int[0];
private static final ReentrantLock LOCK = new ReentrantLock();
private final ServiceLoader<LdapDnsProvider> providers;
/**
@ -75,9 +76,12 @@ final class LdapDnsProviderService {
*/
static LdapDnsProviderService getInstance() {
if (service != null) return service;
synchronized (LOCK) {
LOCK.lock();
try {
if (service != null) return service;
service = new LdapDnsProviderService();
} finally {
LOCK.unlock();
}
return service;
}
@ -96,13 +100,16 @@ final class LdapDnsProviderService {
{
LdapDnsProviderResult result = null;
Hashtable<?, ?> envCopy = new Hashtable<>(env);
synchronized (LOCK) {
LOCK.lock();
try {
Iterator<LdapDnsProvider> iterator = providers.iterator();
while (result == null && iterator.hasNext()) {
result = iterator.next().lookupEndpoints(url, envCopy)
.filter(r -> !r.getEndpoints().isEmpty())
.orElse(null);
}
} finally {
LOCK.unlock();
}
if (result == null) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2023, 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
@ -29,8 +29,8 @@ import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import javax.naming.CommunicationException;
import javax.naming.NamingException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
final class LdapRequest {
@ -46,6 +46,8 @@ final class LdapRequest {
private volatile boolean closed;
private volatile boolean completed;
private final boolean pauseAfterReceipt;
// LdapRequest instance lock
private final ReentrantLock lock = new ReentrantLock();
LdapRequest(int msgId, boolean pause, int replyQueueCapacity) {
this.msgId = msgId;
@ -62,16 +64,23 @@ final class LdapRequest {
replies.offer(EOF);
}
synchronized void close() {
void close() {
lock.lock();
try {
closed = true;
replies.offer(EOF);
} finally {
lock.unlock();
}
}
private boolean isClosed() {
return closed && (replies.size() == 0 || replies.peek() == EOF);
}
synchronized boolean addReplyBer(BerDecoder ber) {
boolean addReplyBer(BerDecoder ber) {
lock.lock();
try {
// check the closed boolean value here as we don't want anything
// to be added to the queue after close() has been called.
if (cancelled || closed) {
@ -96,6 +105,9 @@ final class LdapRequest {
}
return pauseAfterReceipt;
} finally {
lock.unlock();
}
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2023, 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
@ -28,6 +28,8 @@ package com.sun.jndi.ldap;
import javax.naming.*;
import javax.naming.directory.*;
import java.util.Hashtable;
import java.util.concurrent.locks.ReentrantLock;
import com.sun.jndi.toolkit.dir.HierMemDirCtx;
/**
@ -394,6 +396,9 @@ final class LdapSchemaCtx extends HierMemDirCtx {
private int port;
private boolean hasLdapsScheme;
// SchemaInfo instance lock
private final ReentrantLock lock = new ReentrantLock();
SchemaInfo(String schemaEntryName, LdapCtx schemaEntry,
LdapSchemaParser parser) {
this.schemaEntryName = schemaEntryName;
@ -404,11 +409,16 @@ final class LdapSchemaCtx extends HierMemDirCtx {
this.hasLdapsScheme = schemaEntry.hasLdapsScheme;
}
synchronized void close() throws NamingException {
void close() throws NamingException {
lock.lock();
try {
if (schemaEntry != null) {
schemaEntry.close();
schemaEntry = null;
}
} finally {
lock.unlock();
}
}
private LdapCtx reopenEntry(Hashtable<?,?> env) throws NamingException {
@ -417,21 +427,31 @@ final class LdapSchemaCtx extends HierMemDirCtx {
env, hasLdapsScheme);
}
synchronized void modifyAttributes(Hashtable<?,?> env,
void modifyAttributes(Hashtable<?,?> env,
ModificationItem[] mods)
throws NamingException {
lock.lock();
try {
if (schemaEntry == null) {
schemaEntry = reopenEntry(env);
}
schemaEntry.modifyAttributes("", mods);
} finally {
lock.unlock();
}
}
synchronized void modifyAttributes(Hashtable<?,?> env, int mod,
void modifyAttributes(Hashtable<?,?> env, int mod,
Attributes attrs) throws NamingException {
lock.lock();
try {
if (schemaEntry == null) {
schemaEntry = reopenEntry(env);
}
schemaEntry.modifyAttributes("", mod, attrs);
} finally {
lock.unlock();
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2023, 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
@ -25,6 +25,8 @@
package com.sun.jndi.ldap.pool;
import java.util.concurrent.locks.ReentrantLock;
/**
* Represents a description of PooledConnection in Connections.
* Contains a PooledConnection, its state (busy, idle, expired), and idle time.
@ -46,6 +48,8 @@ final class ConnectionDesc {
private byte state = IDLE; // initial state
private long idleSince;
private long useCount = 0; // for stats & debugging only
// ConnectionDesc instance lock
private final ReentrantLock lock = new ReentrantLock();
ConnectionDesc(PooledConnection conn) {
this.conn = conn;
@ -82,7 +86,9 @@ final class ConnectionDesc {
* records the current time so that we will know how long it has been idle.
* @return true if state change occurred.
*/
synchronized boolean release() {
boolean release() {
lock.lock();
try {
d("release()");
if (state == BUSY) {
state = IDLE;
@ -92,6 +98,9 @@ final class ConnectionDesc {
} else {
return false; // Connection wasn't busy to begin with
}
} finally {
lock.unlock();
}
}
/**
@ -100,7 +109,9 @@ final class ConnectionDesc {
*
* @return ConnectionDesc's PooledConnection if it was idle; null otherwise.
*/
synchronized PooledConnection tryUse() {
PooledConnection tryUse() {
lock.lock();
try {
d("tryUse()");
if (state == IDLE) {
@ -110,6 +121,9 @@ final class ConnectionDesc {
}
return null;
} finally {
lock.unlock();
}
}
/**
@ -121,7 +135,9 @@ final class ConnectionDesc {
*
* @return true if entry is idle and has expired; false otherwise.
*/
synchronized boolean expire(long threshold) {
boolean expire(long threshold) {
lock.lock();
try {
if (state == IDLE && idleSince < threshold) {
d("expire(): expired");
@ -134,6 +150,9 @@ final class ConnectionDesc {
d("expire(): not expired");
return false; // Expiration did not occur
}
} finally {
lock.unlock();
}
}
public String toString() {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2023, 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
@ -36,7 +36,6 @@ import java.lang.ref.SoftReference;
import javax.naming.NamingException;
import javax.naming.InterruptedNamingException;
import javax.naming.CommunicationException;
/**
* Represents a list of PooledConnections (actually, ConnectionDescs) with the
@ -318,9 +317,14 @@ final class Connections implements PoolCallback {
* and leave indicator so that any in-use connections will be closed upon
* their return.
*/
synchronized void close() {
void close() {
lock.lock();
try {
expire(System.currentTimeMillis()); // Expire idle connections
closed = true; // Close in-use connections when they are returned
} finally {
lock.unlock();
}
}
String getStats() {
@ -330,7 +334,8 @@ final class Connections implements PoolCallback {
long use = 0;
int len;
synchronized (this) {
lock.lock();
try {
len = conns.size();
ConnectionDesc entry;
@ -348,6 +353,8 @@ final class Connections implements PoolCallback {
++expired;
}
}
} finally {
lock.unlock();
}
return "size=" + len + "; use=" + use + "; busy=" + busy
+ "; idle=" + idle + "; expired=" + expired;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2023, 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
@ -94,6 +94,7 @@ public final class Pool {
private final int prefSize; // preferred num of identical conn per pool
private final int initSize; // initial number of identical conn to create
private final Map<Object, ConnectionsRef> map;
private final ReentrantLock mapLock = new ReentrantLock();
public Pool(int initSize, int prefSize, int maxSize) {
map = new WeakHashMap<>();
@ -125,8 +126,11 @@ public final class Pool {
d("get(): ", id);
if (debug) {
synchronized (map) {
mapLock.lock();
try {
d("size: ", map.size());
} finally {
mapLock.unlock();
}
remaining = checkRemaining(start, remaining);
}
@ -159,7 +163,8 @@ public final class Pool {
throws NamingException {
Connections conns;
synchronized (map) {
mapLock.lock();
try {
ConnectionsRef ref = map.get(id);
if (ref != null) {
return ref.getConnections();
@ -179,6 +184,8 @@ public final class Pool {
// Keep the weak reference through the element of a linked list
weakRefs.add(weakRef);
} finally {
mapLock.unlock();
}
return conns;
}
@ -234,8 +241,11 @@ public final class Pool {
*/
public void expire(long threshold) {
Collection<ConnectionsRef> copy;
synchronized (map) {
mapLock.lock();
try {
copy = new ArrayList<>(map.values());
} finally {
mapLock.unlock();
}
ArrayList<ConnectionsRef> removed = new ArrayList<>();
@ -248,8 +258,11 @@ public final class Pool {
}
}
synchronized (map) {
mapLock.lock();
try {
map.values().removeAll(removed);
} finally {
mapLock.unlock();
}
expungeStaleConnections();
@ -288,7 +301,8 @@ public final class Pool {
out.println("preferred pool size: " + prefSize);
out.println("initial pool size: " + initSize);
synchronized (map) {
mapLock.lock();
try {
out.println("current pool size: " + map.size());
for (Map.Entry<Object, ConnectionsRef> entry : map.entrySet()) {
@ -296,14 +310,19 @@ public final class Pool {
conns = entry.getValue().getConnections();
out.println(" " + id + ":" + conns.getStats());
}
} finally {
mapLock.unlock();
}
out.println("====== Pool end =====================");
}
public String toString() {
synchronized (map) {
return super.toString() + " " + map.toString();
mapLock.lock();
try {
return super.toString() + " " + map;
} finally {
mapLock.unlock();
}
}