Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: File descriptor leaks for eventfd and timerfd_create #1158

Merged
merged 2 commits into from
Nov 6, 2023

Conversation

tyler92
Copy link
Contributor

@tyler92 tyler92 commented Aug 4, 2023

There was no close call for the file descriptors created with eventfd and timerfd_create. It led to a file descriptors leak. I'm really not sure if the fix may have a bad impact, but at least there is no leak now.

@codecov
Copy link

codecov bot commented Aug 4, 2023

Codecov Report

Attention: 9 lines in your changes are missing coverage. Please review.

Comparison is base (fe5639a) 77.92% compared to head (92973b3) 78.11%.
Report is 1 commits behind head on master.

❗ Current head 92973b3 differs from pull request most recent head e6f27c2. Consider uploading reports for the commit e6f27c2 to get more accurate results

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1158      +/-   ##
==========================================
+ Coverage   77.92%   78.11%   +0.19%     
==========================================
  Files          53       53              
  Lines        6990     7074      +84     
==========================================
+ Hits         5447     5526      +79     
- Misses       1543     1548       +5     
Files Coverage Δ
include/pistache/http.h 86.48% <ø> (ø)
include/pistache/http_header.h 91.56% <100.00%> (-0.25%) ⬇️
include/pistache/http_headers.h 78.57% <ø> (-0.92%) ⬇️
include/pistache/os.h 100.00% <ø> (ø)
src/common/mime.cc 88.88% <100.00%> (-1.17%) ⬇️
src/common/os.cc 61.45% <100.00%> (+1.33%) ⬆️
src/server/endpoint.cc 87.95% <100.00%> (+0.45%) ⬆️
src/common/http_headers.cc 86.41% <96.55%> (+5.96%) ⬆️
src/common/http.cc 75.46% <83.33%> (+0.10%) ⬆️
src/common/http_header.cc 91.74% <92.59%> (+0.07%) ⬆️

... and 1 file with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@tyler92
Copy link
Contributor Author

tyler92 commented Aug 4, 2023

ABI check has failed due to the destructor. I would appreciate any help with it. Also, some clangs check failed, but it looks like the failures aren't related to the PR.

@tyler92
Copy link
Contributor Author

tyler92 commented Aug 4, 2023

Probably related: #44

@kiplingw
Copy link
Member

kiplingw commented Aug 4, 2023

Hey @tyler92. Thanks for your PR. Two requests:

  • Can you please bump the patch version because of the addition of new symbols?
  • Can you please add a unit test confirming the leaked descriptor? It's difficult to devise a test that can detect if a file descriptor leaked, but as long as running it under valgrind --track-fds=eys confirms the leak, then that is a good start to replicate the problem and verify that your PR has solved it.

@tyler92
Copy link
Contributor Author

tyler92 commented Aug 5, 2023

@kiplingw Done. I've implemented a simple check of the /proc/self/fd directory with a message about Valgrind as a fallback. This test passed with the fix and failed without it. The Valgrind report without the fix just for reference:

Valgrind report
==3110541== 
==3110541== FILE DESCRIPTORS: 16 open (3 std) at exit.
==3110541== Open file descriptor 33:
==3110541==    at 0x4E5FE8B: eventfd (syscall-template.S:120)
==3110541==    by 0x1809D7: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&)::{lambda()#1}::operator()() const [clone .constprop.0] (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x180C12: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x19635F: Pistache::Tcp::Listener::runThreaded() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1334AC: http_server_test_http_server_is_not_leaked_Test::TestBody() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x4945704: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x493D99A: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4917677: testing::Test::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4918192: testing::TestInfo::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4918ADF: testing::TestSuite::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4928F5D: testing::internal::UnitTestImpl::RunAllTests() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x49467AB: bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541== 
==3110541== Open file descriptor 32:
==3110541==    at 0x4E6102B: timerfd_create (syscall-template.S:120)
==3110541==    by 0x192B39: Pistache::Http::TransportImpl::registerPoller(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B1DF1: Pistache::Aio::AsyncImpl::addHandler(std::shared_ptr<Pistache::Aio::Handler> const&, bool) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1977B0: Pistache::Tcp::Listener::bind(Pistache::Address const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x193D2B: Pistache::Http::Endpoint::serveThreaded() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1334AC: http_server_test_http_server_is_not_leaked_Test::TestBody() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x4945704: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x493D99A: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4917677: testing::Test::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4918192: testing::TestInfo::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4918ADF: testing::TestSuite::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4928F5D: testing::internal::UnitTestImpl::RunAllTests() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541== 
==3110541== Open file descriptor 31:
==3110541==    at 0x4E5FE8B: eventfd (syscall-template.S:120)
==3110541==    by 0x1809D7: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&)::{lambda()#1}::operator()() const [clone .constprop.0] (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x180C12: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x192B2A: Pistache::Http::TransportImpl::registerPoller(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B1DF1: Pistache::Aio::AsyncImpl::addHandler(std::shared_ptr<Pistache::Aio::Handler> const&, bool) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1977B0: Pistache::Tcp::Listener::bind(Pistache::Address const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x193D2B: Pistache::Http::Endpoint::serveThreaded() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1334AC: http_server_test_http_server_is_not_leaked_Test::TestBody() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x4945704: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x493D99A: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4917677: testing::Test::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4918192: testing::TestInfo::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541== 
==3110541== Open file descriptor 27:
==3110541==    at 0x4E6102B: timerfd_create (syscall-template.S:120)
==3110541==    by 0x192B39: Pistache::Http::TransportImpl::registerPoller(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B1DF1: Pistache::Aio::AsyncImpl::addHandler(std::shared_ptr<Pistache::Aio::Handler> const&, bool) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1977B0: Pistache::Tcp::Listener::bind(Pistache::Address const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x193D2B: Pistache::Http::Endpoint::serveThreaded() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1334AC: http_server_test_http_server_is_not_leaked_Test::TestBody() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x4945704: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x493D99A: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4917677: testing::Test::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4918192: testing::TestInfo::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4918ADF: testing::TestSuite::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4928F5D: testing::internal::UnitTestImpl::RunAllTests() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541== 
==3110541== Open file descriptor 26:
==3110541==    at 0x4E5FE8B: eventfd (syscall-template.S:120)
==3110541==    by 0x1809D7: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&)::{lambda()#1}::operator()() const [clone .constprop.0] (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x180C12: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x192B2A: Pistache::Http::TransportImpl::registerPoller(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B1DF1: Pistache::Aio::AsyncImpl::addHandler(std::shared_ptr<Pistache::Aio::Handler> const&, bool) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1977B0: Pistache::Tcp::Listener::bind(Pistache::Address const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x193D2B: Pistache::Http::Endpoint::serveThreaded() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1334AC: http_server_test_http_server_is_not_leaked_Test::TestBody() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x4945704: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x493D99A: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4917677: testing::Test::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4918192: testing::TestInfo::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541== 
==3110541== Open file descriptor 22:
==3110541==    at 0x4E6102B: timerfd_create (syscall-template.S:120)
==3110541==    by 0x192B39: Pistache::Http::TransportImpl::registerPoller(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B1DF1: Pistache::Aio::AsyncImpl::addHandler(std::shared_ptr<Pistache::Aio::Handler> const&, bool) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1977B0: Pistache::Tcp::Listener::bind(Pistache::Address const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x193D2B: Pistache::Http::Endpoint::serveThreaded() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1334AC: http_server_test_http_server_is_not_leaked_Test::TestBody() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x4945704: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x493D99A: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4917677: testing::Test::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4918192: testing::TestInfo::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4918ADF: testing::TestSuite::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4928F5D: testing::internal::UnitTestImpl::RunAllTests() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541== 
==3110541== Open file descriptor 21:
==3110541==    at 0x4E5FE8B: eventfd (syscall-template.S:120)
==3110541==    by 0x1809D7: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&)::{lambda()#1}::operator()() const [clone .constprop.0] (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x180C12: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x192B2A: Pistache::Http::TransportImpl::registerPoller(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B1DF1: Pistache::Aio::AsyncImpl::addHandler(std::shared_ptr<Pistache::Aio::Handler> const&, bool) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1977B0: Pistache::Tcp::Listener::bind(Pistache::Address const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x193D2B: Pistache::Http::Endpoint::serveThreaded() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1334AC: http_server_test_http_server_is_not_leaked_Test::TestBody() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x4945704: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x493D99A: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4917677: testing::Test::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4918192: testing::TestInfo::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541== 
==3110541== Open file descriptor 17:
==3110541==    at 0x4E6102B: timerfd_create (syscall-template.S:120)
==3110541==    by 0x192B39: Pistache::Http::TransportImpl::registerPoller(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B1DF1: Pistache::Aio::AsyncImpl::addHandler(std::shared_ptr<Pistache::Aio::Handler> const&, bool) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1977B0: Pistache::Tcp::Listener::bind(Pistache::Address const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x193D2B: Pistache::Http::Endpoint::serveThreaded() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1334AC: http_server_test_http_server_is_not_leaked_Test::TestBody() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x4945704: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x493D99A: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4917677: testing::Test::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4918192: testing::TestInfo::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4918ADF: testing::TestSuite::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4928F5D: testing::internal::UnitTestImpl::RunAllTests() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541== 
==3110541== Open file descriptor 16:
==3110541==    at 0x4E5FE8B: eventfd (syscall-template.S:120)
==3110541==    by 0x1809D7: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&)::{lambda()#1}::operator()() const [clone .constprop.0] (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x180C12: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x192B2A: Pistache::Http::TransportImpl::registerPoller(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B1DF1: Pistache::Aio::AsyncImpl::addHandler(std::shared_ptr<Pistache::Aio::Handler> const&, bool) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1977B0: Pistache::Tcp::Listener::bind(Pistache::Address const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x193D2B: Pistache::Http::Endpoint::serveThreaded() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1334AC: http_server_test_http_server_is_not_leaked_Test::TestBody() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x4945704: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x493D99A: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4917677: testing::Test::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4918192: testing::TestInfo::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541== 
==3110541== Open file descriptor 12:
==3110541==    at 0x4E5FE8B: eventfd (syscall-template.S:120)
==3110541==    by 0x1809D7: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&)::{lambda()#1}::operator()() const [clone .constprop.0] (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x180C12: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B246F: Pistache::Aio::AsyncImpl::AsyncImpl(Pistache::Aio::Reactor*, unsigned long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B05BD: Pistache::Aio::AsyncContext::makeImpl(Pistache::Aio::Reactor*) const (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B0053: Pistache::Aio::Reactor::init(Pistache::Aio::ExecutionContext const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x19774B: Pistache::Tcp::Listener::bind(Pistache::Address const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x193D2B: Pistache::Http::Endpoint::serveThreaded() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1334AC: http_server_test_http_server_is_not_leaked_Test::TestBody() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x4945704: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x493D99A: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4917677: testing::Test::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541== 
==3110541== Open file descriptor 10:
==3110541==    at 0x4E5FE8B: eventfd (syscall-template.S:120)
==3110541==    by 0x1809D7: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&)::{lambda()#1}::operator()() const [clone .constprop.0] (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x180C12: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B246F: Pistache::Aio::AsyncImpl::AsyncImpl(Pistache::Aio::Reactor*, unsigned long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B05BD: Pistache::Aio::AsyncContext::makeImpl(Pistache::Aio::Reactor*) const (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B0053: Pistache::Aio::Reactor::init(Pistache::Aio::ExecutionContext const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x19774B: Pistache::Tcp::Listener::bind(Pistache::Address const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x193D2B: Pistache::Http::Endpoint::serveThreaded() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1334AC: http_server_test_http_server_is_not_leaked_Test::TestBody() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x4945704: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x493D99A: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4917677: testing::Test::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541== 
==3110541== Open file descriptor 8:
==3110541==    at 0x4E5FE8B: eventfd (syscall-template.S:120)
==3110541==    by 0x1809D7: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&)::{lambda()#1}::operator()() const [clone .constprop.0] (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x180C12: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B246F: Pistache::Aio::AsyncImpl::AsyncImpl(Pistache::Aio::Reactor*, unsigned long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B05BD: Pistache::Aio::AsyncContext::makeImpl(Pistache::Aio::Reactor*) const (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B0053: Pistache::Aio::Reactor::init(Pistache::Aio::ExecutionContext const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x19774B: Pistache::Tcp::Listener::bind(Pistache::Address const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x193D2B: Pistache::Http::Endpoint::serveThreaded() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1334AC: http_server_test_http_server_is_not_leaked_Test::TestBody() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x4945704: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x493D99A: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4917677: testing::Test::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541== 
==3110541== Open file descriptor 6:
==3110541==    at 0x4E5FE8B: eventfd (syscall-template.S:120)
==3110541==    by 0x1809D7: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&)::{lambda()#1}::operator()() const [clone .constprop.0] (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x180C12: Pistache::NotifyFd::bind(Pistache::Polling::Epoll&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B246F: Pistache::Aio::AsyncImpl::AsyncImpl(Pistache::Aio::Reactor*, unsigned long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B05BD: Pistache::Aio::AsyncContext::makeImpl(Pistache::Aio::Reactor*) const (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1B0053: Pistache::Aio::Reactor::init(Pistache::Aio::ExecutionContext const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x19774B: Pistache::Tcp::Listener::bind(Pistache::Address const&) (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x193D2B: Pistache::Http::Endpoint::serveThreaded() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x1334AC: http_server_test_http_server_is_not_leaked_Test::TestBody() (in /home/misha/github.com/pistacheio/pistache/build/tests/run_http_server_test)
==3110541==    by 0x4945704: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x493D99A: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /usr/local/lib/libgtest.so.1.13.0)
==3110541==    by 0x4917677: testing::Test::Run() (in /usr/local/lib/libgtest.so.1.13.0)
==3110541== 
==3110541== 
==3110541== HEAP SUMMARY:
==3110541==     in use at exit: 0 bytes in 0 blocks
==3110541==   total heap usage: 646 allocs, 646 frees, 184,453 bytes allocated
==3110541== 
==3110541== All heap blocks were freed -- no leaks are possible
==3110541== 
==3110541== For lists of detected and suppressed errors, rerun with: -s
==3110541== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

I see the test http_server_test.multiple_client_with_requests_to_multithreaded_server is failed due to timeout, but I'm not sure if this is related to my changes

Copy link
Member

@Tachi107 Tachi107 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @tyler92, thank you for this patch. I'm not (yet) familiar with this part of the codebase, so I can't say for sure if this is the right fix for the problem, but it looks like a sensible solution to me.

I'd appreciate if someone with some experience with these kinds of file descriptors would give some approval here, but if not, I think we can merge this anyway. If some issue is discovered, this patch can always be reverted and reworked.

@kiplingw
Copy link
Member

kiplingw commented Aug 5, 2023

I was going to say the same thing. It seems fine, but it's an area of the code base I'm less familiar with. @dennisjenkins75 thoughts?

@tyler92
Copy link
Contributor Author

tyler92 commented Oct 24, 2023

@kiplingw @dennisjenkins75 Hi. Do we have any chance to review/merge it?

@kiplingw
Copy link
Member

I'm fine with it. But while we wait for @dennisjenkins75's feedback, I'd suggest bumping the patch version in version.txt because the one you've got now is already old in lieu of a recent PR.

Copy link
Member

@kiplingw kiplingw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be updated in lieu of the most recent merged PR.

@tyler92 tyler92 force-pushed the fix_fd_leaks branch 2 times, most recently from d0eef78 to f148290 Compare October 31, 2023 12:44
@tyler92 tyler92 requested a review from kiplingw October 31, 2023 13:37
@tyler92
Copy link
Contributor Author

tyler92 commented Oct 31, 2023

This needs to be updated in lieu of the most recent merged PR.

Done

@kiplingw
Copy link
Member

Hey @tyler92. Thank you for your PR. While we await @dennisjenkins75, I can probably anticipate at least some of his feedback: I think it would be prudent if you were able to add a unit test somehow to verify the leak and the fix.

You might have to be creative by monitoring /proc/self/fd. Note that trying to open anything there, there will be, of course, an additional FD created by trying to introspect. So you'll have to think about that.

@tyler92
Copy link
Contributor Author

tyler92 commented Oct 31, 2023

Hey @tyler92. Thank you for your PR. While we await @dennisjenkins75, I can probably anticipate at least some of his feedback: I think it would be prudent if you were able to add a unit test somehow to verify the leak and the fix.

You might have to be creative by monitoring /proc/self/fd. Note that trying to open anything there, there will be, of course, an additional FD created by trying to introspect. So you'll have to think about that.

Oh, yeah, thanks. For some reason, I lost my unit test while rebasing my branch. Please find the unit test in the latest push. The test failed without the patch, and it's passed with it.

}
else
{
std::cout << "NOTE: Please use Valgrind with '--track-fds=yes' option for this test" << std::endl;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tyler92, I am a bit concerned about this. Does the user need to run the unit test in valgrind(1) in order to verify that it passed? The reason I ask is it would be cumbersome for a user to have to manually do that, as opposed to the build environment automatically doing that. If it can't do that, then if the test fails we'll never know during CI.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you. It's related to the first question from another thread. The logic here is the following - we need to handle the case when our "leak detector" fails. In this case, we can do one of the following: fail the test, pass the test, or pass with a warning. I chose the last option, but if we have full confidence in this leak detection approach, we may fail the test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't have full confidence yet in this approach and it might need refactoring. You might need to modify the build environment itself to ensure that the test is run via valgrind(1). You'd then need to add the valgrind package to debian/control in the Build-Dep stanza as well as making sure the build environment finds it at configure time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kiplingw Probably there is a misunderstanding. When I said "leak detection approach", I meant not valgrind, but checking file descriptors count. Now I suggest removing anything about Valgrind in my test, and it will look like this:

TEST(http_server_test, http_server_is_not_leaked)
{
    const auto number_of_fds = [] {
        using filesystem::directory_iterator;
        const filesystem::path fds_dir { "/proc/self/fd" };

        if (!filesystem::exists(fds_dir))
        {
            return directory_iterator::difference_type(0);
        }

        return std::distance(directory_iterator(fds_dir), directory_iterator {});
    };

    const auto fds_before = number_of_fds();
    ASSERT_GT(fds_before, 0);

    // ...

    const auto fds_after = number_of_fds();
    ASSERT_EQ(fds_before, fds_after);

Valgrind was a fallback option in my original idea, but it has downsides.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dennisjenkins75, thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you probably intend to remove the Valgrind fallback this isn't very relevant, but in general when skipping tests in GoogleTest you should use GTEST_SKIP() (documented at https://google.github.io/googletest/advanced.html#skipping-test-execution) since it will properly signal to the testing environment that a test case was skipped.

server->shutdown();
server.reset();

if (fds_before > 0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Under what conditions would you expect number_of_fds() to return 0? gunit's main() does not close STDOUT nor STDERR, so there should always be at least two.
  2. Why test for this condition AFTER running the HTTP server? Why not test for it on line 1069?
  3. Do you think that it is important to process an actual HTTP request within this test?
    4

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. The answer is simple - only if /proc/self/fd directory doesn't exist. I know that the library supports only Linux now, but I wasn't sure if I could make this assumption for unit tests. Do you recommend removing this check?
  2. There is no significant reason, just to make the unit test less noisy
  3. For this particular leak - it doesn't matter. Regarding leaks in general, I think it's important to test a realistic use case. So if you recommend adding it - I will

@dennisjenkins75
Copy link
Collaborator

Suggestion:

  1. Add a unit test helper library (that is only linked into unit tests).
  2. In that library, add the function for "getting count of open file descriptors for 'self'."
    • In the future, if we ever port to a non-Linux OS, we'll abstract the function here.
  3. Add a unit test for that function alone.
    • Call it twice in a row, should get same result.
    • Call it once, then open a file, call it again, expect count to go up one, close the file, call again, expect count to go down one.
  4. Use the new library function in your intended unit test.
  5. Remove any code about "use valgrind".... Valgrind and other canonical leak detectors are a given for hunting down leaks. Adding that here makes it a bit too prescritive.

@kiplingw
Copy link
Member

kiplingw commented Nov 2, 2023

And just so you know @tyler92, we're not trying to give you the runaround. Your PR in principal makes sense. We just want to make sure we don't make new problems.

@tyler92
Copy link
Contributor Author

tyler92 commented Nov 5, 2023

@kiplingw @dennisjenkins75 I've added new target tests/helpers with the function for getting a count of open file descriptors and tests. I'm not sure about the style for the copyright header, CMakeLists.txt, and meson.build, so please let me know if you don't like something (even minor things)

@kiplingw
Copy link
Member

kiplingw commented Nov 5, 2023

I can't see any immediate issues that stand out, but interested to hear what @dennisjenkins75 says.

Copy link
Collaborator

@dennisjenkins75 dennisjenkins75 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@kiplingw kiplingw merged commit 1fb3465 into pistacheio:master Nov 6, 2023
22 of 36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants