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