3

Do current C++ standards provide any tools to close a program from anywhere (e.g. from inside a function, like exit()), but respecting the resources set aside on the heap?

I know that the exit() function should free all memory (or not, Google gives both answers), but in my case it is necessary to run destructors that will free external system resources.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Fidor
  • 121
  • 1
  • 10
  • `like exit()` it is exit() – KamilCuk Jul 27 '23 at 06:31
  • 1
    On an operating system like Linux, macOS and Windows (and some others) anything allocated or created through the operating system will be released and destroyed when the process exits, by the operating system itself. With that said, it's a good habit to clean up after one self, by deleting what have been new'd etc. Proper resource handling, destructors and use of standard containers and smart pointers etc. all help with that. – Some programmer dude Jul 27 '23 at 06:36
  • On other operating systems, typically for embedded systems or niche operating systems, the operating system might not free resources automatically. On the other hand, on embedded systems the processes and programs are usually not supposed to exit. – Some programmer dude Jul 27 '23 at 06:38
  • Put a print in your dtor, run the program. What do you see? Or are you looking for [signal handlers](https://en.cppreference.com/w/cpp/utility/program/signal)? – Friedrich Jul 27 '23 at 06:46
  • 2
    Ok, i understand that exit() free everything, what operating system allocated, but what about a more complex case where freeing resources is not enough (figuratively speaking, the destructor is responsible for sending "stop rotating" to the physical component or sends a memory release message to device components that are beyond the control of the operating system (e.g. a microcontroller connected via USB) ) – Fidor Jul 27 '23 at 06:54
  • 5
    Throw an exception and catch it in `main`. Of course you need to write everything to be exception-safe. – n. m. could be an AI Jul 27 '23 at 07:01
  • 3
    @Fidor Physical component must be controlled by a driver. Process terminated, file descriptor closed, release handler invoked. – dimich Jul 27 '23 at 07:02
  • 3
    If you need to clear up resources at `exit`, there is the appropriately named [`atexit` function](https://en.cppreference.com/w/cpp/utility/program/atexit) for registering such use. Useful for things like flushing changes to disk or removing temporary files – Homer512 Jul 27 '23 at 07:14
  • 1
    @Homer512 Arguably that is a "C" style backward compatibility approach. I wwould advise the exception safe approach, and rely on C++'s RAII principles. – Pepijn Kramer Jul 27 '23 at 07:21
  • 1
    Whenever your program exits normally (e.g. via call to[std::exit](https://en.cppreference.com/w/cpp/utility/program/exit)), destructors of objects wit static storage duration will be called. Then, if they already do what you need, you are good. If you have some dynamic resources to clean, you can register `std::atexit` like suggested previously. And in any case, if you deal with the hardware, better register a signal handler, or some other stop condition to exit your application. – pptaszni Jul 27 '23 at 07:32
  • 1
    [`exit()` doesn't execute destructors](https://stackoverflow.com/q/2668075/509868) – anatolyg Jul 27 '23 at 07:45
  • @anatolyg That question is about objects on the stack. At least in my experience, it does run destructors for static objects. The real trick is to make sure `abort()` doesn't get called. – user207421 Jul 27 '23 at 08:00
  • There are compiler specific extensions, e.g. for `gcc` that allow you to set up functions to run before `main()` and then after `main()` exits. See [GCC 13 Manual - function attributes, constructor, destructor](https://gcc.gnu.org/onlinedocs/gcc-13.1.0/gcc/Common-Function-Attributes.html) – David C. Rankin Jul 27 '23 at 08:31
  • Thanks for all replies @n. m. will see y'all on Reddit I think that its the simplest and the best solution in my case and that solve my problem – Fidor Jul 27 '23 at 09:10

2 Answers2

1

Wrap the main logic in main with try and create catch(terminateException e), in the catch scope you don't have to write anything. the program will finish correctly and call d'tors of all objects recursively, and if you have free for every malloc or delete for every new, you should have no memory leak.

Guy Sadoun
  • 427
  • 6
  • 17
1

Assuming you have some static and dynamic storage duration entities like this (simplified example with just one translation unit):

struct DriverStatic
{
    DriverStatic(int id): _id{id} {}
    ~DriverStatic() { std::cout << "DriverStatic stopping: " << _id << std::endl; }
    int _id;
};

static DriverStatic ds1{1}, ds2{2};

struct DriverDynamic
{
    DriverDynamic(int id): _id{id} {}
    ~DriverDynamic() { std::cout << "DriverDynamic stopping: " << _id << std::endl; }
    int _id;
};

DriverDynamic* dn1 = new DriverDynamic{1};
DriverDynamic* dn2 = new DriverDynamic{2};

You can register both (one or more) exit handler and (one) terminate handler:

int main()
{
    const int atex_result = std::atexit([]() {
        std::cout << "atexit\n";
        delete dn1;
        delete dn2;
    });
    if (atex_result != 0) {
        std::cerr << "Registration failed\n";
        return EXIT_FAILURE;
    }
    std::set_terminate([](){
        std::cout << "Unhandled exception\n" << std::flush;
        delete dn1;
        delete dn2;
        // not sure how to force static objects destruction
        std::abort();
    });
    // throw std::runtime_error("abnormal exit");
    return 0;
}

This way when std::exit() is called, you will first destroy your drivers with dynamic storage duration, and then drivers with static storage duration will be destroyed in unspecified order.

In case of abnormal program termination, like uncaught exception, your terminate_handler will fire and will attempt to clean the dynamic storage.

Demo

I am not sure however, how to ensure correct static objects cleanup in the abnormal situation. And be aware that your system might still fail to do what you want, if you invoke some undefined behaviour, like throwing from the destructors of your drivers.

In any case, if you are controlling some safety-critical hardware, don't rely solely on your program being able to stop moving elements. Provide a heartbeat mechanism, and interlock your system when the software connection is lost.

pptaszni
  • 5,591
  • 5
  • 27
  • 43