From a4570992d760645ebc723cda32e4623496b69f93 Mon Sep 17 00:00:00 2001
From: Quanah Gibson-Mount <quanah@openldap.org>
Date: Wed, 1 Jul 2009 19:01:58 +0000
Subject: [PATCH] ITS#5836,ITS#6089

---
 CHANGES                |  2 ++
 servers/slapd/result.c | 26 +++++++++++++++++---------
 servers/slapd/slap.h   |  1 +
 3 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/CHANGES b/CHANGES
index 961d69eab8..ebb822784d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -15,8 +15,10 @@ OpenLDAP 2.4.17 Engineering
 	Fixed slapd bind race condition (ITS#6189)
 	Fixed slapd cancel behavior (ITS#6137)
 	Fixed slapd cert validation (ITS#6098)
+	Fixed slapd connection_destroy assert (ITS#6089)
 	Fixed slapd errno handling (ITS#6037)
 	Fixed slapd global alloc handling (ITS#6054)
+	Fixed slapd hung writers (ITS#5836)
 	Fixed slapd ldapi issues (ITS#6056)
 	Fixed slapd moduleload with static backends and modules (ITS#6016)
 	Fixed slapd normalization of updated schema attributes (ITS#5540)
diff --git a/servers/slapd/result.c b/servers/slapd/result.c
index 4b222da640..3ce9e29bd9 100644
--- a/servers/slapd/result.c
+++ b/servers/slapd/result.c
@@ -139,40 +139,46 @@ static long send_ldap_ber(
 	Connection *conn = op->o_conn;
 	ber_len_t bytes;
 	long ret = 0;
-	int closing = 0;
 
 	ber_get_option( ber, LBER_OPT_BER_BYTES_TO_WRITE, &bytes );
 
 	/* write only one pdu at a time - wait til it's our turn */
 	ldap_pvt_thread_mutex_lock( &conn->c_write1_mutex );
-	if (( op->o_abandon && !op->o_cancel ) || !connection_valid( conn )) {
+	if (( op->o_abandon && !op->o_cancel ) || !connection_valid( conn ) ||
+		conn->c_writers < 0 ) {
 		ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
 		return 0;
 	}
-	while ( conn->c_writers > 0 ) {
+
+	conn->c_writers++;
+
+	while ( conn->c_writers > 0 && conn->c_writing ) {
 		ldap_pvt_thread_cond_wait( &conn->c_write1_cv, &conn->c_write1_mutex );
 	}
+
 	/* connection was closed under us */
 	if ( conn->c_writers < 0 ) {
-		closing = 1;
 		/* we're the last waiter, let the closer continue */
 		if ( conn->c_writers == -1 )
 			ldap_pvt_thread_cond_signal( &conn->c_write1_cv );
-	}
-
-	conn->c_writers++;
-
-	if ( closing ) {
+		conn->c_writers++;
 		ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
 		return 0;
 	}
 
+	/* Our turn */
+	conn->c_writing = 1;
+
 	/* write the pdu */
 	while( 1 ) {
 		int err;
 
 		/* lock the connection */ 
 		if ( ldap_pvt_thread_mutex_trylock( &conn->c_mutex )) {
+			if ( !connection_valid(conn)) {
+				ret = 0;
+				break;
+			}
 			ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
 			ldap_pvt_thread_mutex_lock( &conn->c_write1_mutex );
 			if ( conn->c_writers < 0 ) {
@@ -201,6 +207,7 @@ static long send_ldap_ber(
 
 		if ( err != EWOULDBLOCK && err != EAGAIN ) {
 			conn->c_writers--;
+			conn->c_writing = 0;
 			ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
 			connection_closing( conn, "connection lost on write" );
 
@@ -225,6 +232,7 @@ static long send_ldap_ber(
 		}
 	}
 
+	conn->c_writing = 0;
 	if ( conn->c_writers < 0 ) {
 		conn->c_writers++;
 		if ( !conn->c_writers )
diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h
index a47d1bb54b..a89d7124da 100644
--- a/servers/slapd/slap.h
+++ b/servers/slapd/slap.h
@@ -2810,6 +2810,7 @@ struct Connection {
 
 	BerElement	*c_currentber;	/* ber we're attempting to read */
 	int			c_writers;		/* number of writers waiting */
+	char		c_writing;		/* someone is writing */
 
 	char		c_sasl_bind_in_progress;	/* multi-op bind in progress */
 	char		c_writewaiter;	/* true if blocked on write */
-- 
GitLab