Reduce reference cycles to improve memory usage
Previously, a MapFuture and its delegate would always form a reference
cycle. If the futures point to large objects, this can result in
significant memory pressure as the cycle will not be freed until GC gets
around to it. Since GC is only triggered according to object counts and
not object sizes, this can increase apparent memory usage a lot in some
cases.
The cycle can be broken as soon as the delegate future has resolved, so
let's do that. (RetryFuture and PollFuture were already doing this, so
it seems there was an oversight specifically for MapFuture.)
Fixes #344
diff --git a/more_executors/_impl/map.py b/more_executors/_impl/map.py
index fc5e868..3a0eb9d 100644
--- a/more_executors/_impl/map.py
+++ b/more_executors/_impl/map.py
@@ -54,6 +54,10 @@
self._delegate,
)
+ # Drop reference to delegate as soon as no longer needed to reduce
+ # unnecessary reference cycles / memory pressure
+ self._set_delegate(None)
+
if delegate.cancelled():
return
@@ -104,7 +108,10 @@
)
def _me_cancel(self):
- return self._delegate.cancel()
+ with self._me_lock:
+ if self._delegate:
+ return self._delegate.cancel()
+ return False
class MapExecutor(CanCustomizeBind, Executor):