Submitted By: Jeremy Huntwork (jhuntwork at linuxfromscratch dot org)
Date: 2008-04-02
Initial Package Version: 4.6.21
Upstream Status: From Upstream
Origin: http://www.oracle.com/technology/products/berkeley-db/xml/update/4.6.21/patch.4.6.21.html
Description: "Fixes a race condition between checkpoint and DB->close which can
result in the checkpoint thread self-deadlocking."

diff -Naur db-4.6.21-orig/dbinc/mp.h db-4.6.21/dbinc/mp.h
--- db-4.6.21-orig/dbinc/mp.h	2007-09-27 11:28:25.000000000 -0400
+++ db-4.6.21/dbinc/mp.h	2008-04-02 22:22:33.000000000 -0400
@@ -639,6 +639,9 @@
  */
 #define	MP_TRUNC_RECOVER	0x01
 
+/* Private flags to DB_MPOOLFILE->close. */
+#define	DB_MPOOL_NOLOCK		0x002	/* Already have mpf locked. */
+
 #if defined(__cplusplus)
 }
 #endif
diff -Naur db-4.6.21-orig/mp/mp_fopen.c db-4.6.21/mp/mp_fopen.c
--- db-4.6.21-orig/mp/mp_fopen.c	2007-05-17 13:18:01.000000000 -0400
+++ db-4.6.21/mp/mp_fopen.c	2008-04-02 22:22:33.000000000 -0400
@@ -888,7 +888,8 @@
 	 * when we try to flush them.
 	 */
 	deleted = 0;
-	MUTEX_LOCK(dbenv, mfp->mutex);
+	if (!LF_ISSET(DB_MPOOL_NOLOCK))
+		MUTEX_LOCK(dbenv, mfp->mutex);
 	if (F_ISSET(dbmfp, MP_MULTIVERSION))
 		--mfp->multiversion;
 	if (--mfp->mpf_cnt == 0 || LF_ISSET(DB_MPOOL_DISCARD)) {
@@ -909,13 +910,19 @@
 			}
 		}
 		if (mfp->block_cnt == 0) {
+			/*
+			 * We should never discard this mp file if our caller
+			 * is holding the lock on it.  See comment in
+			 * __memp_sync_file.
+			 */
+			DB_ASSERT(dbenv, !LF_ISSET(DB_MPOOL_NOLOCK));
 			if ((t_ret =
 			    __memp_mf_discard(dbmp, mfp)) != 0 && ret == 0)
 				ret = t_ret;
 			deleted = 1;
 		}
 	}
-	if (!deleted)
+	if (!deleted && !LF_ISSET(DB_MPOOL_NOLOCK))
 		MUTEX_UNLOCK(dbenv, mfp->mutex);
 
 done:	/* Discard the DB_MPOOLFILE structure. */
diff -Naur db-4.6.21-orig/mp/mp_sync.c db-4.6.21/mp/mp_sync.c
--- db-4.6.21-orig/mp/mp_sync.c	2007-06-01 14:32:44.000000000 -0400
+++ db-4.6.21/mp/mp_sync.c	2008-04-02 22:22:33.000000000 -0400
@@ -755,7 +755,8 @@
 	 * This is important since we are called with the hash bucket
 	 * locked.  The mfp will get freed via the cleanup pass.
 	 */
-	if (dbmfp != NULL && (t_ret = __memp_fclose(dbmfp, 0)) != 0 && ret == 0)
+	if (dbmfp != NULL &&
+	    (t_ret = __memp_fclose(dbmfp, DB_MPOOL_NOLOCK)) != 0 && ret == 0)
 		ret = t_ret;
 
 	--mfp->mpf_cnt;
