MEDIUM: memory: make pool_gc() run under thread isolation

pool_gc() causes quite some stress on the memory allocator because
it calls a lot of free() calls while other threads are using malloc().
In addition, pool_gc() needs to take care of possible locking because
it may be called from pool allocators. One way to avoid all this is to
use thread_isolate() to make sure the gc runs alone. By putting less
pressure on the pools and getting rid of the locks, it may even take
less time to complete.
diff --git a/src/memory.c b/src/memory.c
index 4a37381..551ca83 100644
--- a/src/memory.c
+++ b/src/memory.c
@@ -248,22 +248,18 @@
 
 /*
  * This function frees whatever can be freed in all pools, but respecting
- * the minimum thresholds imposed by owners. It takes care of avoiding
- * recursion because it may be called from a signal handler.
- *
- * <pool_ctx> is unused
+ * the minimum thresholds imposed by owners. It makes sure to be alone to
+ * run by using thread_isolate(). <pool_ctx> is unused.
  */
 void pool_gc(struct pool_head *pool_ctx)
 {
-	static int recurse;
-	int cur_recurse = 0;
 	struct pool_head *entry;
+	int isolated = thread_isolated();
 
-	if (recurse || !_HA_ATOMIC_CAS(&recurse, &cur_recurse, 1))
-		return;
+	if (!isolated)
+		thread_isolate();
 
 	list_for_each_entry(entry, &pools, list) {
-		HA_SPIN_LOCK(POOL_LOCK, &entry->flush_lock);
 		while ((int)((volatile int)entry->allocated - (volatile int)entry->used) > (int)entry->minavail) {
 			struct pool_free_list cmp, new;
 
@@ -280,10 +276,10 @@
 			free(cmp.free_list);
 			_HA_ATOMIC_SUB(&entry->allocated, 1);
 		}
-		HA_SPIN_UNLOCK(POOL_LOCK, &entry->flush_lock);
 	}
 
-	_HA_ATOMIC_STORE(&recurse, 0);
+	if (!isolated)
+		thread_release();
 }
 
 /* frees an object to the local cache, possibly pushing oldest objects to the
@@ -412,43 +408,31 @@
 
 /*
  * This function frees whatever can be freed in all pools, but respecting
- * the minimum thresholds imposed by owners. It takes care of avoiding
- * recursion because it may be called from a signal handler.
- *
- * <pool_ctx> is used when pool_gc is called to release resources to allocate
- * an element in __pool_refill_alloc. It is important because <pool_ctx> is
- * already locked, so we need to skip the lock here.
+ * the minimum thresholds imposed by owners. It makes sure to be alone to
+ * run by using thread_isolate(). <pool_ctx> is unused.
  */
 void pool_gc(struct pool_head *pool_ctx)
 {
-	static int recurse;
-	int cur_recurse = 0;
 	struct pool_head *entry;
+	int isolated = thread_isolated();
 
-	if (recurse || !_HA_ATOMIC_CAS(&recurse, &cur_recurse, 1))
-		return;
+	if (!isolated)
+		thread_isolate();
 
 	list_for_each_entry(entry, &pools, list) {
 		void *temp;
 		//qfprintf(stderr, "Flushing pool %s\n", entry->name);
-		if (entry != pool_ctx)
-			HA_SPIN_LOCK(POOL_LOCK, &entry->lock);
 		while (entry->free_list &&
 		       (int)(entry->allocated - entry->used) > (int)entry->minavail) {
 			temp = entry->free_list;
 			entry->free_list = *POOL_LINK(entry, temp);
 			entry->allocated--;
-			if (entry != pool_ctx)
-				HA_SPIN_UNLOCK(POOL_LOCK, &entry->lock);
 			pool_free_area(temp, entry->size + POOL_EXTRA);
-			if (entry != pool_ctx)
-				HA_SPIN_LOCK(POOL_LOCK, &entry->lock);
 		}
-		if (entry != pool_ctx)
-			HA_SPIN_UNLOCK(POOL_LOCK, &entry->lock);
 	}
 
-	_HA_ATOMIC_STORE(&recurse, 0);
+	if (!isolated)
+		thread_release();
 }
 #endif