From 1cfaafa3ab3d5f5f1f1dec4153be81dfdefe747e Mon Sep 17 00:00:00 2001
From: Noriko Hosoi <nhosoi@redhat.com>
Date: Fri, 26 Apr 2013 15:24:22 -0700
Subject: [PATCH 1/2] Ticket #47347 - Simple paged results should support
 async search

Bug description: Simple paged results serialized the request
even for a series of asynchronous search requests, and it
returned error 53 (unwilling to perform) if the second request
comes in while the first one is being processed.

Fix description: This patch implements the asynchronous support
for the Simple paged results search.
- Removed pagedresults_check_or_set_processing which was
  used to Simple paged results requests exclusive.  Instead,
  pagedresults_lock is introduced to protect the PagedResults
  object from the other threads sharing the same cookie.
- If any error including hitting the sizelimit or timelimit,
  search result set was released immediately in ldbm_back_
  next_search_entry_ext, which could cause the race condition
  among multiple asynchronous search requests.  To prevent it,
  the search result set is untouched if the operation is a
  Simple paged result search, and let its clean up function
  to handle it.
- Sizelimit was evaluated in the accumulative way instead of
  on the each page size.  For instance, if the sizelimit was
  101 AND the page size is 100, as soon as getting the 2nd page,
  it hit the sizelimit and the search failed.  This patch fixes
  it so that as long as the requested page size is less than 101,
  the requests successfully continue without getting an error 4
  (LDAP_SIZELIMIT_EXCEEDED).  To fulfill the requirement, the
  current size needs to be managed per operation instead of the
  search result set or PagedResults object. For the purpose,
  introduced o_pagedresults_sizelimit to Slapi_Operation.
- When shutting down, connection_table_free could use backend
  callback (e.g., be_search_results_release). Therefore, moved
  be_cleanupall after connection_table_free.
- Each Simple paged results helper functions checks if the
  operation is really a Simple paged result request control is
  associated with to prevent any unexpected behaviour.
---
 ldap/servers/slapd/back-ldbm/filterindex.c |  11 +-
 ldap/servers/slapd/back-ldbm/ldbm_search.c | 117 ++++++++++----------
 ldap/servers/slapd/back-ldbm/vlv.c         |   2 +-
 ldap/servers/slapd/daemon.c                |  13 ++-
 ldap/servers/slapd/operation.c             |   9 +-
 ldap/servers/slapd/opshared.c              |  87 +++++++++------
 ldap/servers/slapd/pagedresults.c          | 165 ++++++++++++++++++++++++++---
 ldap/servers/slapd/proto-slap.h            |  46 +++++---
 ldap/servers/slapd/slap.h                  |   2 +
 ldap/servers/slapd/sort.c                  |   6 +-
 10 files changed, 321 insertions(+), 137 deletions(-)

diff --git a/ldap/servers/slapd/back-ldbm/filterindex.c b/ldap/servers/slapd/back-ldbm/filterindex.c
index 865f8ab..64b4e2d 100644
--- a/ldap/servers/slapd/back-ldbm/filterindex.c
+++ b/ldap/servers/slapd/back-ldbm/filterindex.c
@@ -317,7 +317,7 @@ ava_candidates(
         if ( unindexed ) {
             unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
             slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
-            pagedresults_set_unindexed( pb->pb_conn, pr_idx );
+            pagedresults_set_unindexed( pb->pb_conn, pb->pb_op, pr_idx );
         }
 
         /* We don't use valuearray_free here since the valueset, berval
@@ -349,7 +349,7 @@ ava_candidates(
         if ( unindexed ) {
             unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
             slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
-            pagedresults_set_unindexed( pb->pb_conn, pr_idx );
+            pagedresults_set_unindexed( pb->pb_conn, pb->pb_op, pr_idx );
         }
          valuearray_free( &ivals );
          LDAPDebug( LDAP_DEBUG_TRACE, "<= ava_candidates %lu\n",
@@ -390,7 +390,7 @@ presence_candidates(
         unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
         slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
         slapi_pblock_get(pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx);
-        pagedresults_set_unindexed( pb->pb_conn, pr_idx );
+        pagedresults_set_unindexed(pb->pb_conn, pb->pb_op, pr_idx);
     }
 
     if (idl != NULL && ALLIDS(idl) && strcasecmp(type, "nscpentrydn") == 0) {
@@ -504,6 +504,7 @@ extensible_candidates(
                                                   SLAPI_PAGED_RESULTS_INDEX,
                                                   &pr_idx );
                                 pagedresults_set_unindexed( glob_pb->pb_conn,
+                                                            glob_pb->pb_op,
                                                             pr_idx );
                             }
                             if (idl2 == NULL)
@@ -951,7 +952,7 @@ substring_candidates(
     slapi_pblock_get(pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx);
     if ( ivals == NULL || *ivals == NULL ) {
         slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
-        pagedresults_set_unindexed( pb->pb_conn, pr_idx );
+        pagedresults_set_unindexed( pb->pb_conn, pb->pb_op, pr_idx );
         LDAPDebug( LDAP_DEBUG_TRACE,
             "<= sub_candidates ALLIDS (no keys)\n", 0, 0, 0 );
         return( idl_allids( be ) );
@@ -965,7 +966,7 @@ substring_candidates(
     idl = keys2idl( be, type, indextype_SUB, ivals, err, &unindexed, &txn, allidslimit );
     if ( unindexed ) {
         slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
-        pagedresults_set_unindexed( pb->pb_conn, pr_idx );
+        pagedresults_set_unindexed( pb->pb_conn, pb->pb_op, pr_idx );
     }
     valuearray_free( &ivals );
 
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_search.c b/ldap/servers/slapd/back-ldbm/ldbm_search.c
index 70a2ee3..be4b63c 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_search.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_search.c
@@ -55,7 +55,7 @@ static int build_candidate_list( Slapi_PBlock *pb, backend *be,
 static IDList *base_candidates( Slapi_PBlock *pb, struct backentry *e );
 static IDList *onelevel_candidates( Slapi_PBlock *pb, backend *be, const char *base, struct backentry *e, Slapi_Filter *filter, int managedsait, int *lookup_returned_allidsp, int *err );
 static back_search_result_set* new_search_result_set(IDList* idl,int vlv, int lookthroughlimit);
-static void delete_search_result_set( back_search_result_set **sr );
+static void delete_search_result_set(Slapi_PBlock *pb, back_search_result_set **sr);
 static int can_skip_filter_test( Slapi_PBlock *pb, struct slapi_filter *f,
         int scope, IDList *idl );
 
@@ -167,6 +167,7 @@ ldbm_back_search_cleanup(Slapi_PBlock *pb,
     int estimate = 0; /* estimated search result count */
     backend *be;
     ldbm_instance *inst;
+    back_search_result_set *sr = NULL;
 
     slapi_pblock_get( pb, SLAPI_BACKEND, &be );
     inst = (ldbm_instance *) be->be_instance_info;
@@ -183,19 +184,14 @@ ldbm_back_search_cleanup(Slapi_PBlock *pb,
     {
         slapi_send_ldap_result( pb, ldap_result, NULL, ldap_result_description, 0, NULL );
     }
-    {
-        /* hack hack --- code to free the result set if we don't need it */
-        /* We get it and check to see if the structure was ever used */
-        back_search_result_set *sr = NULL;
-        slapi_pblock_get( pb, SLAPI_SEARCH_RESULT_SET, &sr );
-        if ( (NULL != sr) && (function_result != 0) ) {
-            int pr_idx = -1;
-            slapi_pblock_get( pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx );
-            /* in case paged results, clean up the conn */
-            pagedresults_set_search_result( pb->pb_conn, NULL, 0, pr_idx );
-            slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
-            slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate );
-            delete_search_result_set(&sr);
+    /* code to free the result set if we don't need it */
+    /* We get it and check to see if the structure was ever used */
+    slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET, &sr);
+    if (sr) {
+        if (function_result) {
+            slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate);
+            slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_ENTRY, NULL);
+            delete_search_result_set(pb, &sr);
         }
     }
     if (vlv_request_control)
@@ -898,7 +894,7 @@ ldbm_back_search( Slapi_PBlock *pb )
 
         slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
         slapi_pblock_get( pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx );
-        pagedresults_set_unindexed( pb->pb_conn, pr_idx );
+        pagedresults_set_unindexed( pb->pb_conn, pb->pb_op, pr_idx );
     }
 
     sr->sr_candidates = candidates;
@@ -1386,13 +1382,24 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
     int                    estimate = 0; /* estimated search result count */
     back_txn               txn = {NULL};
     int                    pr_idx = -1;
+    Slapi_Connection       *conn;
+    Slapi_Operation        *op;
 
+    slapi_pblock_get( pb, SLAPI_SEARCH_TARGET_SDN, &basesdn );
+    if (NULL == basesdn) {
+        slapi_send_ldap_result( pb, LDAP_INVALID_DN_SYNTAX, NULL,
+                               "Null target DN", 0, NULL );
+        return( -1 );
+    }
+    slapi_pblock_get( pb, SLAPI_SEARCH_RESULT_SET, &sr );
+    if (NULL == sr) {
+        goto bail;
+    }
     slapi_pblock_get( pb, SLAPI_BACKEND, &be );
     slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
     slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope );
     slapi_pblock_get( pb, SLAPI_MANAGEDSAIT, &managedsait );
     slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &filter );
-    slapi_pblock_get( pb, SLAPI_SEARCH_TARGET_SDN, &basesdn );
     slapi_pblock_get( pb, SLAPI_NENTRIES, &nentries );
     slapi_pblock_get( pb, SLAPI_SEARCH_SIZELIMIT, &slimit );
     slapi_pblock_get( pb, SLAPI_SEARCH_TIMELIMIT, &tlimit );
@@ -1400,32 +1407,30 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
     slapi_pblock_get( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
     slapi_pblock_get( pb, SLAPI_SEARCH_REFERRALS, &urls );
     slapi_pblock_get( pb, SLAPI_TARGET_UNIQUEID, &target_uniqueid );
-    slapi_pblock_get( pb, SLAPI_SEARCH_RESULT_SET, &sr );
     slapi_pblock_get( pb, SLAPI_TXN, &txn.back_txn_txn );
-    slapi_pblock_get( pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx );
+    slapi_pblock_get( pb, SLAPI_CONNECTION, &conn );
+    slapi_pblock_get( pb, SLAPI_OPERATION, &op );
 
     if ( !txn.back_txn_txn ) {
         dblayer_txn_init( li, &txn );
         slapi_pblock_set( pb, SLAPI_TXN, txn.back_txn_txn );
     }
 
-    if (NULL == sr) {
-        goto bail;
-    }
-
     if (sr->sr_norm_filter) {
         int val = 1;
         slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FILTER_NORMALIZED, &val );
         filter = sr->sr_norm_filter;
     }
 
-    if (NULL == basesdn) {
-        slapi_send_ldap_result( pb, LDAP_INVALID_DN_SYNTAX, NULL,
-                               "Null target DN", 0, NULL );
-        return( -1 );
-    }
-
-    if (sr->sr_current_sizelimit >= 0) {
+    if (op_is_pagedresults(op)) {
+        int myslimit;
+        /* On Simple Paged Results search, sizelimit is appied for each page. */
+        slapi_pblock_get(pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx);
+        myslimit = pagedresults_get_sizelimit(conn, op, pr_idx);
+        if (myslimit >= 0) {
+            slimit = myslimit;
+        }
+    } else if (sr->sr_current_sizelimit >= 0) {
         /* 
          * sr_current_sizelimit contains the current sizelimit.
          * In case of paged results, getting one page is one operation,
@@ -1442,8 +1447,7 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
     /* Return to the cache the entry we handed out last time */
     /* If we are using the extension, the front end will tell
      * us when to do this so we don't do it now */
-    if ( !use_extension )
-    {
+    if (sr->sr_entry && !use_extension) {
         CACHE_RETURN( &inst->inst_cache, &(sr->sr_entry) );
         sr->sr_entry = NULL;
     }
@@ -1467,15 +1471,12 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
         /* check for abandon */
         if ( slapi_op_abandoned( pb ) || (NULL == sr) )
         {
-            /* in case paged results, clean up the conn */
-            pagedresults_set_search_result( pb->pb_conn, NULL, 0, pr_idx );
-            slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate );
             if ( use_extension ) {
                 slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, NULL );
             }
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, NULL );
-            delete_search_result_set( &sr );
+            delete_search_result_set(pb, &sr);
             rc = SLAPI_FAIL_GENERAL;
             goto bail;
         }
@@ -1484,15 +1485,12 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
         curtime = current_time();
         if ( tlimit != -1 && curtime > stoptime )
         {
-            /* in case paged results, clean up the conn */
-            pagedresults_set_search_result( pb->pb_conn, NULL, 0, pr_idx );
-            slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate );
             if ( use_extension ) {
                 slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, NULL );
             } 
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, NULL );
-            delete_search_result_set( &sr );
+            delete_search_result_set(pb, &sr);
             rc = SLAPI_FAIL_GENERAL;
             slapi_send_ldap_result( pb, LDAP_TIMELIMIT_EXCEEDED, NULL, NULL, nentries, urls );
             goto bail;
@@ -1501,15 +1499,12 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
         /* check lookthrough limit */
         if ( llimit != -1 && sr->sr_lookthroughcount >= llimit )
         {
-            /* in case paged results, clean up the conn */
-            pagedresults_set_search_result( pb->pb_conn, NULL, 0, pr_idx );
-            slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate );
             if ( use_extension ) {
                 slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, NULL );
             } 
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, NULL );
-            delete_search_result_set( &sr );
+            delete_search_result_set(pb, &sr);
             rc = SLAPI_FAIL_GENERAL;
             slapi_send_ldap_result( pb, LDAP_ADMINLIMIT_EXCEEDED, NULL, NULL, nentries, urls );
             goto bail;
@@ -1521,15 +1516,12 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
         {
             /* No more entries */
             /* destroy back_search_result_set */
-            /* in case paged results, clean up the conn */
-            pagedresults_set_search_result( pb->pb_conn, NULL, 0, pr_idx );
-            slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate );
             if ( use_extension ) {
                 slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, NULL );
             }
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, NULL );
-            delete_search_result_set( &sr );
+            delete_search_result_set(pb, &sr);
             rc = 0;
             goto bail;
         }
@@ -1662,17 +1654,20 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
                  {
                      if ( --slimit < 0 ) {
                          CACHE_RETURN( &inst->inst_cache, &e );
-                         /* in case paged results, clean up the conn */
-                         pagedresults_set_search_result( pb->pb_conn, NULL,
-                                                         0, pr_idx);
-                         slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
                          slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate );
-                         delete_search_result_set( &sr );
+                         delete_search_result_set(pb, &sr);
                          slapi_send_ldap_result( pb, LDAP_SIZELIMIT_EXCEEDED, NULL, NULL, nentries, urls );
                          rc = SLAPI_FAIL_GENERAL;
                          goto bail;
                      }
                      slapi_pblock_set( pb, SLAPI_SEARCH_SIZELIMIT, &slimit );
+                     if (op_is_pagedresults(op)) {
+                         /* 
+                          * On Simple Paged Results search,
+                          * sizelimit is appied to each page.
+                          */
+                         pagedresults_set_sizelimit(conn, op, slimit, pr_idx);
+                     }
                      sr->sr_current_sizelimit = slimit;
                  }
                  if ( (filter_test != 0) && sr->sr_virtuallistview)
@@ -1779,13 +1774,20 @@ new_search_result_set(IDList *idl, int vlv, int lookthroughlimit)
 }
 
 static void
-delete_search_result_set( back_search_result_set **sr )
+delete_search_result_set( Slapi_PBlock *pb, back_search_result_set **sr )
 {
     int rc = 0, filt_errs = 0;
-    if ( NULL == sr || NULL == *sr)
+    if ( NULL == sr || NULL == *sr )
     {
         return;
     }
+    if (pb) {
+        if (op_is_pagedresults(pb->pb_op)) {
+            /* If the op is pagedresults, let the module clean up sr. */
+            return;
+        }
+        slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_SET, NULL);
+    }
     if ( NULL != (*sr)->sr_candidates )
     {
         idl_free( (*sr)->sr_candidates );
@@ -1800,12 +1802,17 @@ delete_search_result_set( back_search_result_set **sr )
     slapi_filter_free((*sr)->sr_norm_filter, 1);
     memset( *sr, 0, sizeof( back_search_result_set ) );
     slapi_ch_free( (void**)sr );
+    return;
 }
 
+/*
+ * This function is called from pagedresults free/cleanup functions.
+ */ 
 void
 ldbm_back_search_results_release( void **sr )
 {
-    delete_search_result_set( (back_search_result_set **)sr );
+    /* passing NULL pb forces to delete the search result set */
+    delete_search_result_set( NULL, (back_search_result_set **)sr );
 }
 
 int
diff --git a/ldap/servers/slapd/back-ldbm/vlv.c b/ldap/servers/slapd/back-ldbm/vlv.c
index 389a5ec..5e5d0a5 100644
--- a/ldap/servers/slapd/back-ldbm/vlv.c
+++ b/ldap/servers/slapd/back-ldbm/vlv.c
@@ -1156,7 +1156,7 @@ vlv_search_build_candidate_list(Slapi_PBlock *pb, const Slapi_DN *base, int *vlv
 	    slapi_pblock_get( pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx );
 	    slapi_rwlock_unlock(be->vlvSearchList_lock);
 	    slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
-	    pagedresults_set_unindexed( pb->pb_conn, pr_idx );
+	    pagedresults_set_unindexed( pb->pb_conn, pb->pb_op, pr_idx );
 	    rc = VLV_FIND_SEARCH_FAILED;
 	} else if((*vlv_rc=vlvIndex_accessallowed(pi, pb)) != LDAP_SUCCESS) {
 	    slapi_rwlock_unlock(be->vlvSearchList_lock);
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
index 962699a..768f7b3 100644
--- a/ldap/servers/slapd/daemon.c
+++ b/ldap/servers/slapd/daemon.c
@@ -1367,14 +1367,19 @@ void slapd_daemon( daemon_ports_t *ports )
 		mapping_tree_free ();
 	}
 
+	/* 
+	 * connection_table_free could use callbacks in the backend.
+	 * (e.g., be_search_results_release)
+	 * Thus, it needs to be called before be_cleanupall.
+	 */
+	connection_table_free(the_connection_table);
+	the_connection_table= NULL;
+
 	be_cleanupall (); 
-    LDAPDebug( LDAP_DEBUG_TRACE, "slapd shutting down - backends closed down\n",
+	LDAPDebug( LDAP_DEBUG_TRACE, "slapd shutting down - backends closed down\n",
 			0, 0, 0 );
 	referrals_free();
 
-	connection_table_free(the_connection_table);
-	the_connection_table= NULL;
-
 	/* tell the time thread to shutdown and then wait for it */
 	time_shutdown = 1;
 	PR_JoinThread( time_thread_p );
diff --git a/ldap/servers/slapd/operation.c b/ldap/servers/slapd/operation.c
index e47e4de..8ac413f 100644
--- a/ldap/servers/slapd/operation.c
+++ b/ldap/servers/slapd/operation.c
@@ -181,10 +181,11 @@ operation_init(Slapi_Operation *o, int flags)
 		o->o_next = NULL;
 		o->o_flags= flags;
 		if ( config_get_accesslog_level() & LDAP_DEBUG_TIMING ) {
-                    o->o_interval = PR_IntervalNow();
-                } else {
-                    o->o_interval = (PRIntervalTime)0;
-                }
+			o->o_interval = PR_IntervalNow();
+		} else {
+			o->o_interval = (PRIntervalTime)0;
+		}
+		o->o_pagedresults_sizelimit = -1;
 	}
 
 }
diff --git a/ldap/servers/slapd/opshared.c b/ldap/servers/slapd/opshared.c
index 42b0935..e7f6135 100644
--- a/ldap/servers/slapd/opshared.c
+++ b/ldap/servers/slapd/opshared.c
@@ -53,7 +53,7 @@ static void compute_limits (Slapi_PBlock *pb);
 
 /* attributes that no clients are allowed to add or modify */
 static char *protected_attrs_all [] = { PSEUDO_ATTR_UNHASHEDUSERPASSWORD,
-					NULL };
+                                        NULL };
 static char *pwpolicy_lock_attrs_all [] = { "passwordRetryCount",
                                             "retryCountResetTime",
                                             "accountUnlockTime",
@@ -239,7 +239,7 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
   int             internal_op;
   Slapi_DN        *basesdn = NULL;
   Slapi_DN        *sdn = NULL;
-  Slapi_Operation *operation;
+  Slapi_Operation *operation = NULL;
   Slapi_Entry     *referral = NULL;
   char            *proxydn = NULL; 
   char            *proxystr = NULL;
@@ -265,7 +265,6 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
   int curr_search_count = 0;
   Slapi_Backend *pr_be = NULL;
   void *pr_search_result = NULL;
-  int pr_reset_processing = 0;
   int pr_idx = -1;
   Slapi_DN *orig_sdn = NULL;
   int free_sdn = 0;
@@ -469,23 +468,16 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
           if ((LDAP_SUCCESS == rc) ||
               (LDAP_CANCELLED == rc) || (0 == pagesize)) {
               unsigned int opnote = SLAPI_OP_NOTE_SIMPLEPAGED;
-              if (pagedresults_check_or_set_processing(pb->pb_conn, pr_idx)) {
-                  send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM,
-                                   NULL, "Simple Paged Results Search "
-                                   "already in progress on this connection",
-                                   0, NULL);
-                  goto free_and_return_nolock;
-              }
-              /* need to reset after we are done with this op */
-              pr_reset_processing = 1;
               op_set_pagedresults(operation);
               pr_be = pagedresults_get_current_be(pb->pb_conn, pr_idx);
               pr_search_result = pagedresults_get_search_result(pb->pb_conn,
+                                                                operation,
                                                                 pr_idx);
               estimate = 
                  pagedresults_get_search_result_set_size_estimate(pb->pb_conn,
+                                                                  operation,
                                                                   pr_idx);
-              if (pagedresults_get_unindexed(pb->pb_conn, pr_idx)) {
+              if (pagedresults_get_unindexed(pb->pb_conn, operation, pr_idx)) {
                   opnote |= SLAPI_OP_NOTE_UNINDEXED;
               }
               slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
@@ -640,7 +632,7 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
     slapi_pblock_get( pb, SLAPI_SEARCH_TIMELIMIT, &tlimit );
     slapi_pblock_get( pb, SLAPI_OPINITIATED_TIME, &optime );
     time_up = (tlimit==-1 ? -1 : optime + tlimit); /* -1: no time limit */
-    pagedresults_set_timelimit(pb->pb_conn, time_up, pr_idx);
+    pagedresults_set_timelimit(pb->pb_conn, pb->pb_op, time_up, pr_idx);
   }
 
   /* PAR: now filters have been rewritten, we can assign plugins to work on them */
@@ -694,12 +686,25 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
     if (op_is_pagedresults(operation) && pr_search_result) {
       void *sr = NULL;
       /* PAGED RESULTS and already have the search results from the prev op */
-      slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, pr_search_result );
-      rc = send_results_ext (pb, 1, &pnentries, pagesize, &pr_stat);
+      pagedresults_lock(pb->pb_conn, pr_idx);
+      /* 
+       * In async paged result case, the search result might be released
+       * by other theads.  We need to double check it in the locked region.
+       */
+      pr_search_result = pagedresults_get_search_result(pb->pb_conn, 
+                                                        operation,
+                                                        pr_idx);
+      if (pr_search_result) {
+        slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, pr_search_result );
+        rc = send_results_ext (pb, 1, &pnentries, pagesize, &pr_stat);
 
-      /* search result could be reset in the backend/dse */
-      slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET, &sr);
-      pagedresults_set_search_result(pb->pb_conn, sr, 0, pr_idx);
+        /* search result could be reset in the backend/dse */
+        slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET, &sr);
+        pagedresults_set_search_result(pb->pb_conn, operation, sr, 0, pr_idx);
+      } else {
+        pr_stat = PAGEDRESULTS_SEARCH_END;
+      }
+      pagedresults_unlock(pb->pb_conn, pr_idx);
 
       if (PAGEDRESULTS_SEARCH_END == pr_stat) {
         /* no more entries to send in the backend */
@@ -720,14 +725,18 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
       }
       pagedresults_set_response_control(pb, 0, estimate,
                                         curr_search_count, pr_idx);
-      if (pagedresults_get_with_sort(pb->pb_conn, pr_idx)) {
+      if (pagedresults_get_with_sort(pb->pb_conn, operation, pr_idx)) {
         sort_make_sort_response_control(pb, CONN_GET_SORT_RESULT_CODE, NULL);
       }
       pagedresults_set_search_result_set_size_estimate(pb->pb_conn, 
+                                                       operation,
                                                        estimate, pr_idx);
       next_be = NULL; /* to break the loop */
       if (curr_search_count == -1) {
-        pagedresults_free_one(pb->pb_conn, pr_idx);
+        pagedresults_lock(pb->pb_conn, pr_idx);
+        slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_SET, NULL);
+        pagedresults_free_one(pb->pb_conn, operation, pr_idx);
+        pagedresults_unlock(pb->pb_conn, pr_idx);
       }
     } else {
       /* be_suffix null means that we are searching the default backend
@@ -849,7 +858,11 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
             int with_sort = operation->o_flags & OP_FLAG_SERVER_SIDE_SORTING;
   
             curr_search_count = pnentries;
+            slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET, &sr);
             if (PAGEDRESULTS_SEARCH_END == pr_stat) {
+              if (sr) { /* in case a left over sr is found, clean it up */
+                be->be_search_results_release(&sr);
+              }
               if (NULL == next_be) {
                 /* no more entries && no more backends */
                 curr_search_count = -1;
@@ -861,24 +874,31 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
               }
             } else {
               curr_search_count = pnentries;
-              slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET, &sr);
               slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate);
-              if (pagedresults_set_current_be(pb->pb_conn, be, pr_idx) < 0 ||
-                  pagedresults_set_search_result(pb->pb_conn, sr, 0, pr_idx) < 0 ||
-                  pagedresults_set_search_result_count(pb->pb_conn,
-                                                   curr_search_count, pr_idx) < 0 ||
-                  pagedresults_set_search_result_set_size_estimate(pb->pb_conn,
-                                                   estimate, pr_idx) < 0 ||
-                  pagedresults_set_with_sort(pb->pb_conn, with_sort, pr_idx) < 0) {
+              pagedresults_lock(pb->pb_conn, pr_idx);
+              if ((pagedresults_set_current_be(pb->pb_conn, be, pr_idx) < 0) ||
+                  (pagedresults_set_search_result(pb->pb_conn, operation,
+                                                  sr, 0, pr_idx) < 0) ||
+                  (pagedresults_set_search_result_count(pb->pb_conn, operation,
+                                                        curr_search_count,
+                                                        pr_idx) < 0) ||
+                  (pagedresults_set_search_result_set_size_estimate(pb->pb_conn,
+                                                                 operation,
+                                                                 estimate,
+                                                                 pr_idx) < 0) ||
+                  (pagedresults_set_with_sort(pb->pb_conn, operation,
+                                              with_sort, pr_idx) < 0)) {
+                pagedresults_unlock(pb->pb_conn, pr_idx);
                 goto free_and_return;
               }
+              pagedresults_unlock(pb->pb_conn, pr_idx);
             }
             pagedresults_set_response_control(pb, 0, estimate, 
                                               curr_search_count, pr_idx);
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
             next_be = NULL; /* to break the loop */
             if (curr_search_count == -1) {
-                pagedresults_free_one(pb->pb_conn, pr_idx);
+                pagedresults_free_one(pb->pb_conn, operation, pr_idx);
             }
         }
   
@@ -1007,9 +1027,6 @@ free_and_return_nolock:
 
   slapi_ch_free_string(&proxydn);
   slapi_ch_free_string(&proxystr);
-  if (pr_reset_processing) {
-    pagedresults_reset_processing(pb->pb_conn, pr_idx);
-  }
 }
 
 /* Returns 1 if this processing on this entry is finished
@@ -1257,7 +1274,7 @@ iterate(Slapi_PBlock *pb, Slapi_Backend *be, int send_result,
 
     *pnentries = 0;
     
-    while (!done) 
+    while (!done)
     {
         Slapi_Entry *gerentry = NULL;
         Slapi_Operation *operation;
@@ -1275,7 +1292,7 @@ iterate(Slapi_PBlock *pb, Slapi_Backend *be, int send_result,
                 operation_out_of_disk_space();
             }
             pr_stat = PAGEDRESULTS_SEARCH_END;
-            pagedresults_set_timelimit(pb->pb_conn, 0, pr_idx);
+            pagedresults_set_timelimit(pb->pb_conn, pb->pb_op, 0, pr_idx);
             rval = -1;
             done = 1;
             continue;
diff --git a/ldap/servers/slapd/pagedresults.c b/ldap/servers/slapd/pagedresults.c
index 9b294eb..78bd6b0 100644
--- a/ldap/servers/slapd/pagedresults.c
+++ b/ldap/servers/slapd/pagedresults.c
@@ -131,6 +131,7 @@ pagedresults_parse_control_value( Slapi_PBlock *pb,
             }
         }
         conn->c_pagedresults.prl_count++;
+        conn->c_pagedresults.prl_list[*index].pr_mutex = PR_NewLock();
     } else {
         /* Repeated paged results request.
          * PagedResults is already allocated. */
@@ -140,6 +141,8 @@ pagedresults_parse_control_value( Slapi_PBlock *pb,
         *index = strtol(ptr, NULL, 10);
         slapi_ch_free_string(&ptr);
     }
+    /* reset sizelimit */
+    op->o_pagedresults_sizelimit = -1;
     slapi_ch_free((void **)&cookie.bv_val);
 
     if ((*index > -1) && (*index < conn->c_pagedresults.prl_maxlen)) {
@@ -243,10 +246,13 @@ bailout:
 }
 
 int 
-pagedresults_free_one( Connection *conn, int index )
+pagedresults_free_one( Connection *conn, Operation *op, int index )
 {
     int rc = -1;
 
+    if (!op_is_pagedresults(op)) {
+        return 0; /* noop */
+    }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "--> pagedresults_free_one: idx=%d\n", index);
     if (conn && (index > -1)) {
@@ -256,13 +262,19 @@ pagedresults_free_one( Connection *conn, int index )
                            "conn=%d paged requests list count is %d\n",
                            conn->c_connid, conn->c_pagedresults.prl_count);
         } else if (index < conn->c_pagedresults.prl_maxlen) {
+            PRLock *prmutex = NULL;
             PagedResults *prp = conn->c_pagedresults.prl_list + index;
             if (prp && prp->pr_current_be &&
                 prp->pr_current_be->be_search_results_release &&
                 prp->pr_search_result_set) {
                 prp->pr_current_be->be_search_results_release(&(prp->pr_search_result_set));
             }
+            if (prp->pr_mutex) {
+                /* pr_mutex is reused; back it up and reset it. */
+                prmutex = prp->pr_mutex;
+            }
             memset(prp, '\0', sizeof(PagedResults));
+            prp->pr_mutex = prmutex;
             conn->c_pagedresults.prl_count--;
             rc = 0;
         }
@@ -348,9 +360,12 @@ pagedresults_set_current_be(Connection *conn, Slapi_Backend *be, int index)
 }
 
 void *
-pagedresults_get_search_result(Connection *conn, int index)
+pagedresults_get_search_result(Connection *conn, Operation *op, int index)
 {
     void *sr = NULL;
+    if (!op_is_pagedresults(op)) {
+        return sr; /* noop */
+    }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "--> pagedresults_get_search_result: idx=%d\n", index);
     if (conn && (index > -1)) {
@@ -366,20 +381,31 @@ pagedresults_get_search_result(Connection *conn, int index)
 }
 
 int
-pagedresults_set_search_result(Connection *conn, void *sr, 
+pagedresults_set_search_result(Connection *conn, Operation *op, void *sr, 
                                int locked, int index)
 {
     int rc = -1;
+    if (!op_is_pagedresults(op)) {
+        return 0; /* noop */
+    }
     LDAPDebug2Args(LDAP_DEBUG_TRACE,
                    "--> pagedresults_set_search_result: idx=%d, sr=%p\n",
                    index, sr);
     if (conn && (index > -1)) {
         if (!locked) PR_Lock(conn->c_mutex);
         if (index < conn->c_pagedresults.prl_maxlen) {
-            conn->c_pagedresults.prl_list[index].pr_search_result_set = sr;
+            if (sr) { /* set */
+                if (NULL ==
+                    conn->c_pagedresults.prl_list[index].pr_search_result_set) {
+                    conn->c_pagedresults.prl_list[index].pr_search_result_set = sr;
+                    rc = 0;
+                }
+            } else {  /* reset */
+                conn->c_pagedresults.prl_list[index].pr_search_result_set = sr;
+                rc = 0;
+            }
         }
         if (!locked) PR_Unlock(conn->c_mutex);
-        rc = 0;
     }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "<-- pagedresults_set_search_result: %d\n", rc);
@@ -387,9 +413,12 @@ pagedresults_set_search_result(Connection *conn, void *sr,
 }
 
 int
-pagedresults_get_search_result_count(Connection *conn, int index)
+pagedresults_get_search_result_count(Connection *conn, Operation *op, int index)
 {
     int count = 0;
+    if (!op_is_pagedresults(op)) {
+        return count; /* noop */
+    }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "--> pagedresults_get_search_result_count: idx=%d\n", index);
     if (conn && (index > -1)) {
@@ -405,9 +434,13 @@ pagedresults_get_search_result_count(Connection *conn, int index)
 }
 
 int
-pagedresults_set_search_result_count(Connection *conn, int count, int index)
+pagedresults_set_search_result_count(Connection *conn, Operation *op,
+                                     int count, int index)
 {
     int rc = -1;
+    if (!op_is_pagedresults(op)) {
+        return rc; /* noop */
+    }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "--> pagedresults_set_search_result_count: idx=%d\n", index);
     if (conn && (index > -1)) {
@@ -424,9 +457,14 @@ pagedresults_set_search_result_count(Connection *conn, int count, int index)
 }
 
 int
-pagedresults_get_search_result_set_size_estimate(Connection *conn, int index)
+pagedresults_get_search_result_set_size_estimate(Connection *conn, 
+                                                 Operation *op,
+                                                 int index)
 {
     int count = 0;
+    if (!op_is_pagedresults(op)) {
+        return count; /* noop */
+    }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "--> pagedresults_get_search_result_set_size_estimate: "
                   "idx=%d\n", index);
@@ -445,9 +483,13 @@ pagedresults_get_search_result_set_size_estimate(Connection *conn, int index)
 
 int
 pagedresults_set_search_result_set_size_estimate(Connection *conn, 
+                                                 Operation *op,
                                                  int count, int index)
 {
     int rc = -1;
+    if (!op_is_pagedresults(op)) {
+        return rc; /* noop */
+    }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "--> pagedresults_set_search_result_set_size_estimate: "
                   "idx=%d\n", index);
@@ -466,9 +508,12 @@ pagedresults_set_search_result_set_size_estimate(Connection *conn,
 }
 
 int
-pagedresults_get_with_sort(Connection *conn, int index)
+pagedresults_get_with_sort(Connection *conn, Operation *op, int index)
 {
     int flags = 0;
+    if (!op_is_pagedresults(op)) {
+        return flags; /* noop */
+    }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "--> pagedresults_get_with_sort: idx=%d\n", index);
     if (conn && (index > -1)) {
@@ -484,9 +529,13 @@ pagedresults_get_with_sort(Connection *conn, int index)
 }
 
 int
-pagedresults_set_with_sort(Connection *conn, int flags, int index)
+pagedresults_set_with_sort(Connection *conn, Operation *op,
+                           int flags, int index)
 {
     int rc = -1;
+    if (!op_is_pagedresults(op)) {
+        return rc; /* noop */
+    }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "--> pagedresults_set_with_sort: idx=%d\n", index);
     if (conn && (index > -1)) {
@@ -505,9 +554,12 @@ pagedresults_set_with_sort(Connection *conn, int flags, int index)
 }
 
 int
-pagedresults_get_unindexed(Connection *conn, int index)
+pagedresults_get_unindexed(Connection *conn, Operation *op, int index)
 {
     int flags = 0;
+    if (!op_is_pagedresults(op)) {
+        return flags; /* noop */
+    }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "--> pagedresults_get_unindexed: idx=%d\n", index);
     if (conn && (index > -1)) {
@@ -523,9 +575,12 @@ pagedresults_get_unindexed(Connection *conn, int index)
 }
 
 int
-pagedresults_set_unindexed(Connection *conn, int index)
+pagedresults_set_unindexed(Connection *conn, Operation *op, int index)
 {
     int rc = -1;
+    if (!op_is_pagedresults(op)) {
+        return rc; /* noop */
+    }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "--> pagedresults_set_unindexed: idx=%d\n", index);
     if (conn && (index > -1)) {
@@ -543,9 +598,12 @@ pagedresults_set_unindexed(Connection *conn, int index)
 }
 
 int
-pagedresults_get_sort_result_code(Connection *conn, int index)
+pagedresults_get_sort_result_code(Connection *conn, Operation *op, int index)
 {
     int code = LDAP_OPERATIONS_ERROR;
+    if (!op_is_pagedresults(op)) {
+        return code; /* noop */
+    }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "--> pagedresults_get_sort_result_code: idx=%d\n", index);
     if (conn && (index > -1)) {
@@ -561,9 +619,13 @@ pagedresults_get_sort_result_code(Connection *conn, int index)
 }
 
 int
-pagedresults_set_sort_result_code(Connection *conn, int code, int index)
+pagedresults_set_sort_result_code(Connection *conn, Operation *op,
+                                  int code, int index)
 {
     int rc = -1;
+    if (!op_is_pagedresults(op)) {
+        return rc; /* noop */
+    }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "--> pagedresults_set_sort_result_code: idx=%d\n", index);
     if (conn && (index > -1)) {
@@ -580,9 +642,13 @@ pagedresults_set_sort_result_code(Connection *conn, int code, int index)
 }
 
 int
-pagedresults_set_timelimit(Connection *conn, time_t timelimit, int index)
+pagedresults_set_timelimit(Connection *conn, Operation *op,
+                           time_t timelimit, int index)
 {
     int rc = -1;
+    if (!op_is_pagedresults(op)) {
+        return rc; /* noop */
+    }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "--> pagedresults_set_timelimit: idx=%d\n", index);
     if (conn && (index > -1)) {
@@ -597,6 +663,35 @@ pagedresults_set_timelimit(Connection *conn, time_t timelimit, int index)
     return rc;
 }
 
+int
+pagedresults_set_sizelimit(Connection *conn, Operation *op,
+                           int sizelimit, int index)
+{
+    int rc = -1;
+    if (!op_is_pagedresults(op)) {
+        return rc; /* noop */
+    }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_set_sizelimit: idx=%d\n", index);
+    op->o_pagedresults_sizelimit = sizelimit;
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE, "<-- pagedresults_set_sizelimit: %d\n", rc);
+    return rc;
+}
+
+int
+pagedresults_get_sizelimit(Connection *conn, Operation *op, int index)
+{
+    int sizelimit = -1;
+    if (!op_is_pagedresults(op)) {
+        return sizelimit; /* noop */
+    }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_get_sizelimit: idx=%d\n", index);
+    sizelimit = op->o_pagedresults_sizelimit;
+    LDAPDebug0Args(LDAP_DEBUG_TRACE, "<-- pagedresults_get_sizelimit\n");
+    return sizelimit;
+}
+
 /*
  * pagedresults_cleanup cleans up the pagedresults list;
  * it does not free the list.
@@ -629,6 +724,9 @@ pagedresults_cleanup(Connection *conn, int needlock)
             prp->pr_current_be->be_search_results_release(&(prp->pr_search_result_set));
             rc = 1;
         }
+        if (prp->pr_mutex) {
+            PR_DestroyLock(prp->pr_mutex);
+        }
         memset(prp, '\0', sizeof(PagedResults));
     }
     conn->c_pagedresults.prl_count = 0;
@@ -665,6 +763,9 @@ pagedresults_cleanup_all(Connection *conn, int needlock)
     for (i = 0; conn->c_pagedresults.prl_list &&
                 i < conn->c_pagedresults.prl_maxlen; i++) {
         prp = conn->c_pagedresults.prl_list + i;
+        if (prp->pr_mutex) {
+            PR_DestroyLock(prp->pr_mutex);
+        }
         if (prp->pr_current_be && prp->pr_search_result_set &&
             prp->pr_current_be->be_search_results_release) {
             prp->pr_current_be->be_search_results_release(&(prp->pr_search_result_set));
@@ -681,6 +782,7 @@ pagedresults_cleanup_all(Connection *conn, int needlock)
     return rc;
 }
 
+#if 0 /* Stopped using it (#47347) */
 /*
  * check to see if this connection is currently processing
  * a pagedresults search - if it is, return True - if not,
@@ -734,6 +836,7 @@ pagedresults_reset_processing(Connection *conn, int index)
                   "<-- pagedresults_reset_processing: %d\n", ret);
     return ret;
 }
+#endif
 
 /* Are all the paged results requests timed out? */
 int
@@ -819,3 +922,35 @@ op_set_pagedresults(Operation *op)
     }
     op->o_flags |= OP_FLAG_PAGED_RESULTS;
 }
+
+/*
+ * pagedresults_lock/unlock -- introduced to protect search results for the
+ * asynchronous searches.
+ */
+void
+pagedresults_lock( Connection *conn, int index )
+{
+    PagedResults *prp;
+    if (!conn || (index < 0) || (index >= conn->c_pagedresults.prl_maxlen)) {
+        return;
+    }
+    prp = conn->c_pagedresults.prl_list + index;
+    if (prp->pr_mutex) {
+        PR_Lock(prp->pr_mutex);
+    }
+    return;
+}
+
+void
+pagedresults_unlock( Connection *conn, int index )
+{
+    PagedResults *prp;
+    if (!conn || (index < 0) || (index >= conn->c_pagedresults.prl_maxlen)) {
+        return;
+    }
+    prp = conn->c_pagedresults.prl_list + index;
+    if (prp->pr_mutex) {
+        PR_Unlock(prp->pr_mutex);
+    }
+    return;
+}
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
index 9b0072b..10dec7a 100644
--- a/ldap/servers/slapd/proto-slap.h
+++ b/ldap/servers/slapd/proto-slap.h
@@ -1442,34 +1442,48 @@ void pagedresults_set_response_control(Slapi_PBlock *pb, int iscritical,
                                        int curr_search_count, int index);
 Slapi_Backend *pagedresults_get_current_be(Connection *conn, int index);
 int pagedresults_set_current_be(Connection *conn, Slapi_Backend *be, int index);
-void *pagedresults_get_search_result(Connection *conn, int index);
-int pagedresults_set_search_result(Connection *conn, void *sr, 
+void *pagedresults_get_search_result(Connection *conn, Operation *op,
+                                     int index);
+int pagedresults_set_search_result(Connection *conn, Operation *op, void *sr, 
                                    int locked, int index);
-int pagedresults_get_search_result_count(Connection *conn, int index);
-int pagedresults_set_search_result_count(Connection *conn, int cnt, int index);
-int pagedresults_get_search_result_set_size_estimate(Connection *conn, 
+int pagedresults_get_search_result_count(Connection *conn, Operation *op,
+                                         int index);
+int pagedresults_set_search_result_count(Connection *conn, Operation *op,                                                int cnt, int index);
+int pagedresults_get_search_result_set_size_estimate(Connection *conn,
+                                                     Operation *op, 
                                                      int index);
-int pagedresults_set_search_result_set_size_estimate(Connection *conn, int cnt, 
+int pagedresults_set_search_result_set_size_estimate(Connection *conn,
+                                                     Operation *op, int cnt, 
                                                      int index);
-int pagedresults_get_with_sort(Connection *conn, int index);
-int pagedresults_set_with_sort(Connection *conn, int flags, int index);
-int pagedresults_get_unindexed(Connection *conn, int index);
-int pagedresults_set_unindexed(Connection *conn, int index);
-int pagedresults_get_sort_result_code(Connection *conn, int index);
-int pagedresults_set_sort_result_code(Connection *conn, int code, int index);
-int pagedresults_set_timelimit(Connection *conn, time_t timelimit, int index);
+int pagedresults_get_with_sort(Connection *conn, Operation *op, int index);
+int pagedresults_set_with_sort(Connection *conn, Operation *op,
+                               int flags, int index);
+int pagedresults_get_unindexed(Connection *conn, Operation *op, int index);
+int pagedresults_set_unindexed(Connection *conn, Operation *op, int index);
+int pagedresults_get_sort_result_code(Connection *conn, Operation *op,
+                                      int index);
+int pagedresults_set_sort_result_code(Connection *conn, Operation *op,
+                                      int code, int index);
+int pagedresults_set_timelimit(Connection *conn, Operation *op,
+                               time_t timelimit, int index);
+int pagedresults_get_sizelimit(Connection *conn, Operation *op, int index);
+int pagedresults_set_sizelimit(Connection *conn, Operation *op,
+                               int sizelimit, int index);
 int pagedresults_cleanup(Connection *conn, int needlock);
+#if 0 /* Stopped using it (#47347) */
 int pagedresults_check_or_set_processing(Connection *conn, int index);
 int pagedresults_reset_processing(Connection *conn, int index);
+#endif
 int pagedresults_is_timedout_nolock(Connection *conn);
 int pagedresults_reset_timedout_nolock(Connection *conn);
 int pagedresults_in_use_nolock(Connection *conn);
-int pagedresults_free_one(Connection *conn, int index);
-int pagedresults_free_one_msgid_nolock( Connection *conn, ber_int_t msgid );
+int pagedresults_free_one(Connection *conn, Operation *op, int index);
+int pagedresults_free_one_msgid_nolock(Connection *conn, ber_int_t msgid);
 int op_is_pagedresults(Operation *op);
 int pagedresults_cleanup_all(Connection *conn, int needlock);
 void op_set_pagedresults(Operation *op);
-
+void pagedresults_lock(Connection *conn, int index);
+void pagedresults_unlock(Connection *conn, int index);
 
 /*
  * sort.c
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
index 1f37e3d..b0ac7b3 100644
--- a/ldap/servers/slapd/slap.h
+++ b/ldap/servers/slapd/slap.h
@@ -1367,6 +1367,7 @@ typedef struct op {
 	unsigned long o_abandoned_op; /* operation abandoned by this operation - used to decide which plugins to invoke */
 	struct slapi_operation_parameters o_params;
 	struct slapi_operation_results o_results;
+	int o_pagedresults_sizelimit;
 } Operation;
 
 /*
@@ -1395,6 +1396,7 @@ typedef struct _paged_results {
     time_t        pr_timelimit;           /* time limit for this request */
     int           pr_flags;
     ber_int_t     pr_msgid;               /* msgid of the request; to abandon */
+    PRLock        *pr_mutex;              /* protect each conn structure    */
 } PagedResults;
 
 /* array of simple paged structure stashed in connection */
diff --git a/ldap/servers/slapd/sort.c b/ldap/servers/slapd/sort.c
index 903fa6f..72c9ec9 100644
--- a/ldap/servers/slapd/sort.c
+++ b/ldap/servers/slapd/sort.c
@@ -58,12 +58,14 @@ sort_make_sort_response_control ( Slapi_PBlock *pb, int code, char *error_type)
     slapi_pblock_get(pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx);
 
     if (code == CONN_GET_SORT_RESULT_CODE) {
-        code = pagedresults_get_sort_result_code(pb->pb_conn, pr_idx);
+        code = pagedresults_get_sort_result_code(pb->pb_conn,
+                                                 pb->pb_op, pr_idx);
     } else {
         Slapi_Operation *operation;
         slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
         if (op_is_pagedresults(operation)) {
-            pagedresults_set_sort_result_code(pb->pb_conn, code, pr_idx);
+            pagedresults_set_sort_result_code(pb->pb_conn,
+                                              pb->pb_op, code, pr_idx);
         }
     }
 
-- 
1.7.11.7

