-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Description
Hi Folly team,
I am experiencing a significant memory leak in my application, which heavily uses folly::Future. After enabling AddressSanitizer (ASAN) with LeakSanitizer (LSan), I've identified that hundreds of thousands of folly::FutureTimeout and folly::FutureCancellation exception objects are being leaked.
I suspect this might be related to how Future::within() handles timeouts, but I am also aware this could be a usage issue in my own code, such as a circular reference. I would greatly appreciate your insights or guidance on this matter.
Problem Description
My application extensively uses Folly's Future timeout features to ensure responsiveness. These leaks are not random; they are always composed of the exception objects that Folly allocates internally to represent timeout or cancellation states.
LSan Reports
I have two distinct stack traces from LSan, one for folly::FutureTimeout and another for folly::FutureCancellation. The call stacks are nearly identical, differing only in the type of exception being created.
Here is the cleaned-up stack trace for the FutureTimeout leak:
Direct leak of 61085952 byte(s) in 424208 object(s) allocated from:
___interceptor_aligned_alloc
/data1/workspace/llvm-project-src/compiler-rt/lib/asan/asan_malloc_linux.cpp:113:3
??
??:0:0
std::exception_ptr folly::make_exception_ptr_with_fn::operator()<auto folly::make_exception_ptr_with_fn::make<folly::FutureTimeout, folly::FutureTimeou...
/data1/workspace/lingbin/xxx/thirdparty/installed/include/folly/lang/Exception.h:568:14
std::exception_ptr folly::make_exception_ptr_with_fn::operator()<folly::FutureTimeout, folly::FutureTimeout>(std::__1::in_place_type_t<folly::FutureTime...
/data1/workspace/lingbin/xxx/thirdparty/installed/include/folly/lang/Exception.h:579:12
folly::exception_wrapper::exception_wrapper<folly::FutureTimeout, folly::FutureTimeout>(folly::exception_wrapper::PrivateCtor, std::__1::in_place_type_...
/data1/workspace/lingbin/xxx/thirdparty/installed/include/folly/ExceptionWrapper-inl.h:69:12
... (stack continues) ...
void folly::futures::detail::FutureBase<folly::Unit>::raise<folly::FutureTimeout>(folly::FutureTimeout&&)
/data1/workspace/lingbin/xxx/thirdparty/installed/include/folly/futures/Future.h:368:11
folly::SemiFuture<folly::Unit> folly::SemiFuture<folly::Unit>::within<folly::FutureTimeout>(std::__1::chrono::duration<long long, std::__1::ratio<1l, 10...
/data1/workspace/lingbin/xxx/thirdparty/installed/include/folly/futures/Future-inl.h:2160:31
... (stack continues to thread pool execution) ...
And here is the stack trace for the FutureCancellation leak:
Direct leak of 100511280 byte(s) in 697995 object(s) allocated from:
___interceptor_aligned_alloc
/data1/workspace/llvm-project-src/compiler-rt/lib/asan/asan_malloc_linux.cpp:113:3
??
??:0:0
std::exception_ptr folly::make_exception_ptr_with_fn::operator()<auto folly::make_exception_ptr_with_fn::make<folly::FutureCancellation, folly::FutureC...
/data1/workspace/lingbin/xxx/thirdparty/installed/include/folly/lang/Exception.h:568:14
... (stack continues) ...
void folly::futures::detail::FutureBase<folly::Unit>::raise<folly::FutureCancellation>(folly::FutureCancellation&&)
/data1/workspace/lingbin/xxx/thirdparty/installed/include/folly/futures/Future.h:368:11
folly::futures::detail::FutureBase<folly::Unit>::cancel()
/data1/workspace/lingbin/xxx/thirdparty/installed/include/folly/futures/Future.h:374:19
folly::SemiFuture<folly::Unit> folly::SemiFuture<folly::Unit>::within<folly::FutureTimeout>(std::__1::chrono::duration<long long, std::__1::ratio<1l, ...
/data1/workspace/lingbin/xxx/thirdparty/installed/include/folly/futures/Future-inl.h:2140:24
... (stack continues to thread pool execution) ...
Question
My understanding is that Folly, as a library, is responsible for the lifecycle of the objects it creates. When a timeout occurs, Folly allocates an exception object to represent this state. If the corresponding Future is properly destructed without its exception being handled, I would expect a std::terminate call, not a memory leak.
The presence of a leak strongly suggests that the Future objects themselves are not being destructed.
While I am actively investigating my codebase for such patterns, my question to the community is:
Is there any known scenario or subtle behavior within Folly's Future::within or its continuation handling that could lead to such a leak?
Any advice on how to best debug such Future-related circular references would also be highly appreciated.
Thanks for your time and for this great library.
Environment:
- Folly version:
2024.06.10.00 - Compiler:
LLVM 17.0.6 - Platform: Linux
- Using __lsan_do_recoverable_leak_check to report leaks.