andreasbuhr / cppcoro

A library of C++ coroutine abstractions for the coroutines TS
MIT License
364 stars 53 forks source link

Memory leak on gcc-10.2 #35

Open lacc97 opened 3 years ago

lacc97 commented 3 years ago

Compiling on gcc-10.2 (Gentoo Linux) the following code

#include <iostream>

#include <cppcoro/sync_wait.hpp>
#include <cppcoro/task.hpp>

static int ii = 0;
auto foo() -> cppcoro::task<int> {
  co_return (++ii);
}

auto process() -> cppcoro::task<int> {
  co_return (co_await foo()) + (co_await foo());
}

auto main() -> int {
  std::cout << cppcoro::sync_wait(process()) << std::endl;
}

with options --std=c++20 -fcoroutines -O2 -lcppcoro -pthread results in memory leaks according to valgrind.

==10070== Memcheck, a memory error detector
==10070== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==10070== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==10070== Command: ./main_2
==10070== 
3
==10070== 
==10070== HEAP SUMMARY:
==10070==     in use at exit: 128 bytes in 2 blocks
==10070==   total heap usage: 6 allocs, 4 frees, 74,048 bytes allocated
==10070== 
==10070== 64 bytes in 1 blocks are definitely lost in loss record 1 of 2
==10070==    at 0x4838E0F: operator new(unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==10070==    by 0x10A6C5: _Z7processv.actor(process()::_Z7processv.frame*) (in main_2)
==10070==    by 0x10AB0E: cppcoro::detail::_ZN7cppcoro6detail19make_sync_wait_taskINS_4taskIiEEiLi0EEENS0_14sync_wait_taskIT0_EEOT_.actor(cppcoro::detail::sync_wait_task<int> cppcoro::detail::make_sync_wait_task<cppcoro::task<int>, int, 0>(cppcoro::task<int>&&)::_ZN7cppcoro6detail19make_sync_wait_taskINS_4taskIiEEiLi0EEENS0_14sync_wait_taskIT0_EEOT_.frame*) (in main_2)
==10070==    by 0x10AD8B: cppcoro::awaitable_traits<cppcoro::task<int>&&, void>::await_result_t cppcoro::sync_wait<cppcoro::task<int> >(cppcoro::task<int>&&) (in main_2)
==10070==    by 0x10A349: main (in main_2)
==10070== 
==10070== 64 bytes in 1 blocks are definitely lost in loss record 2 of 2
==10070==    at 0x4838E0F: operator new(unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==10070==    by 0x10A710: _Z7processv.actor(process()::_Z7processv.frame*) (in main_2)
==10070==    by 0x10AB0E: cppcoro::detail::_ZN7cppcoro6detail19make_sync_wait_taskINS_4taskIiEEiLi0EEENS0_14sync_wait_taskIT0_EEOT_.actor(cppcoro::detail::sync_wait_task<int> cppcoro::detail::make_sync_wait_task<cppcoro::task<int>, int, 0>(cppcoro::task<int>&&)::_ZN7cppcoro6detail19make_sync_wait_taskINS_4taskIiEEiLi0EEENS0_14sync_wait_taskIT0_EEOT_.frame*) (in main_2)
==10070==    by 0x10AD8B: cppcoro::awaitable_traits<cppcoro::task<int>&&, void>::await_result_t cppcoro::sync_wait<cppcoro::task<int> >(cppcoro::task<int>&&) (in main_2)
==10070==    by 0x10A349: main (in main_2)
==10070== 
==10070== LEAK SUMMARY:
==10070==    definitely lost: 128 bytes in 2 blocks
==10070==    indirectly lost: 0 bytes in 0 blocks
==10070==      possibly lost: 0 bytes in 0 blocks
==10070==    still reachable: 0 bytes in 0 blocks
==10070==         suppressed: 0 bytes in 0 blocks
==10070== 
==10070== For lists of detected and suppressed errors, rerun with: -s
==10070== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

Maybe it's a gcc bug? The cppcoro task implementation seems correct but I'm no expert.

go2sh commented 3 years ago

I would guess its not a gcc bug. You have to destroy coroutine_handles. They don't free the memory, when the object it destroyed. This can easily lead to memory leaks. I think, the destroy is missing in cppcoro.