Alanyy
Alanyy

Reputation: 23

How to handle infinite loop in threads and avoid memory leak

I have a project which run several infinite loops in threads, I simplify it to the following code:

#include <iostream>
#include <vector>
#include <thread>
#include <boost/fiber/algo/round_robin.hpp>
#include <boost/thread.hpp>
#include <chrono> 
#include <boost/thread.hpp>
#include <string>

void foo(){
    std::cout<<"thread a"<<std::endl;
    while(true){
        std::this_thread::sleep_for(std::chrono::seconds{5});
    }
    return;
}
void foo2(){
    std::cout<<"thread b"<<std::endl;
    while(true){
        std::this_thread::sleep_for(std::chrono::seconds{5});
    }
    return;
}

int main(){
    std::thread a(foo);
    std::thread b(foo2);
    while(true){
        std::this_thread::sleep_for(std::chrono::seconds{5});
    }
    return 0;
}

It works as expected. I use valgrind to detect memory leak and it shows it has memory leak(I guess infinite loop never release memory because it never stops). I considered to use join(), but it doesn't make sense here. I tried to add

a.detach();
b.detach();

before the while loop in main function, but it doesn't solve memory leak issue.

Would somebody please give me some advice how to avoid memory leak here?

Upvotes: 1

Views: 1060

Answers (2)

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122458

Its a long answer, so I'll start with a summary: The leak in your example code is not an issue. Nevertheless you should fix it. And the way to fix it is to turn the infinite loops into non-infinite loops and to join the threads.


A memory leak is for example this:

void bar() {
    int * x = new int;
}

An object is dynamically allocated and when the function returns all pointers to the object are lost. The memory is still allocated to the process but you cannot free it. Calling bar many times will pile up memory until the process runs out of memory and gets killed. This is to be avoided.

Then there is a less severe type of memory leaks:

 int main() {
      bar();
 }

Here some memory is allocated, but next the process terminates. When the process terminates all memory is reclaimed by the OS. The missing delete is not such a big issue here.

There are other ways of leaking memory and I am not trying to enumerate them all, but rather use the examples to get a point across.

Then there are good reasons to worry also about this second type of leaks, that I called "less severe". And that is because it is typically not just memory that is leaked. Consider (dont write code like this! it is only for illustrating a point):

   int main() {
       A* = new A();
   }

A is some class. In main some memory is allocated and an A is constructed. The memory is the lesser problem here. The real problem is any other resource that A claimed in its constructor. It might have opened a file. It might have opened a connection to a data base. Such resources must be cleaned up in a destructor. If the A object is not properly destroyed critical data might get lost.

Conclusion: Leaking memory when returning from main isn't a big issue. Leaking other resource is a big issue. And the memory leak is good indication that also other resources are not cleaned up properly.

In your toy example there is no problem but only a small change makes your approach problematic:

void foo(){
    A a;
    while(true){
        std::this_thread::sleep_for(std::chrono::seconds{5});
    }
}

A is again the class that acquires some resource in its constructor and that resouce must be properly release in the destructor. Also when the program terminates you want to have the data in the database, the last log message in the log file, etc.

Rather than while(true) and detach you should use some atomic or condition variable to signal the threads that they should stop. Something along the line of

std::atomic<bool> foo_runs;

void foo(){
    A a;
    while(foo_runs.load()){
        std::this_thread::sleep_for(std::chrono::seconds{5});
    }
}

int main() {
    foo_runs.store(true);
    std::thread a(foo);
    // do something else 
    
    foo_runs.store(false);
    a.join();
}

Upvotes: 2

user10
user10

Reputation: 296

Whatever you do, you have to join()/detach() on a and b. If you call join() before the main loop, you'll never get to the main loop. If you get to the end of main() without join()/detach(), std::abort() will be called.

I don't see a leak, but there is a race on the cout stream. Maybe potential leak can happen if detached thread a or b escapes main() and continues running a never-ending function. In such case, the thread itself is leaked since it is detached from *this (main), and there is no owner to destroy it. If that's the story, try to call join() on both a and b after the main loop.

Upvotes: 1

Related Questions