Thread: clean up memory when catching errors?

  1. #1
    Registered User
    Join Date
    Sep 2016
    Posts
    2

    clean up memory when catching errors?

    Hi there,

    i wonder how you guys keep your code AND your memory clean at the same time. since i am working myself through a OpenCL tutorial i found that there is a lot of initialization to be done. each step might end up exiting the main function. what to do with all the stuff allocated before? i tried to add up all necessary cleaning commands within each catch of an error, but somehow that makes the code look a bit bloated.

    as an example i'll list a part of my code:
    Code:
    // retreive OpenCL device CPU or GPU    
       if (clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_GPU, 1, &device_id, NULL) != CL_SUCCESS) {
            printf("unable to get device_id\n");
            return 1;
        }
    
    
    // create context
        // intenionally set context properties NULL, to keep sample code small
        context = clCreateContext(NULL, 1, &device_id, NULL, NULL, NULL);
        if (err != CL_SUCCESS) {
            printf("unable to create context. ERROR CODE: %i\n", err);
            return 1;
        }
    
    
    // create command queue
        command_queue = clCreateCommandQueue(context, device_id, 0, &err);
        if (err != CL_SUCCESS) {
            printf("unable to create command queue\n");
    
    
            // we should start cleaning up here, since we created a context before..
            clReleaseContext(context);
    
    
            return 1;
        }
    
    
    // create program object
        program = clCreateProgramWithSource(context, 1, &ProgramSource, NULL, &err);
        if (err != CL_SUCCESS) {
            printf("unable to create program\n");
    
    
            // cleaning up what we already have
            clReleaseCommandQueue(command_queue);
            clReleaseContext(context);
    
    
            return 1;
        }
    as you can see, the further i get, the more clRelease... stuff will add up. so at some point it will look like this (which i find is very much):
    Code:
    // set kernel arguments    
       if (clSetKernelArg(kernel, 0, sizeof(cl_mem), &data_buffer_1) ||
            clSetKernelArg(kernel, 1, sizeof(cl_mem), &data_buffer_2) != CL_SUCCESS) {
            printf("unable to set kernel args\n");
    
    
            // cleaning up what we already have
            clReleaseMemObject(data_buffer_1);
            clReleaseMemObject(data_buffer_2);
            clReleaseKernel(kernel);
            clReleaseProgram(program);
            clReleaseCommandQueue(command_queue);
            clReleaseContext(context);
    
    
            return 1;
       }
    while reading the tutorial i found, that the sample code just cleans up at the end of the main function if everything goes well, but i think that will leave some garbage at the memory in case some error occurs (right?).

    thanks in advance for your contribution

    kind regards
    christian

  2. #2
    Registered User
    Join Date
    Sep 2007
    Posts
    1,012
    In general when the program exits, all memory will be reclaimed by the OS, so it's typically not something you need to worry about. That said, it's not bad practice to do so, and if this were a function you were calling without exiting the program, then it'd definitely be a problem if it leaked memory.

    C doesn't make this sort of thing easy the way a language like C++ does (with RAII). In C this is typically done with goto:

    Code:
    int ret = 0;
    
    a = acquire();
    if(a is error) {
      ret = -1;
      goto out1;
    }
    
    b = acquire();
    if(b is error) {
      ret = -2;
      goto out2;
    }
    
    c = acquire();
    if(c is error) {
      ret = -3;
      goto out3;
    }
    
    ...
    
    release(c);
    
    out3:
    release(b);
    
    out2:
    release(a);
    
    out1:
    return ret;
    It's tedious and error prone, but that's kind of what you get with C. Of course you'd have to adapt the above to the function at hand, e.g. should it always release memory or is it returning allocated memory, etc.

  3. #3
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Something like this might work.
    Code:
    ret = 1;  // error return code
    
    a = getA(&err);
    if (err) goto getA_failed;
    
    b = getB(&err);
    if (err) goto getB_failed;
    
    c = getC(&err);
    if (err) goto getC_failed;
    
    //...
    
    ret = 0;  // success
    
    
    getC_failed:
        releaseC(c);
    getB_failed:
        releaseB(b);
    getA_failed:
        releaseA(a);
    
    return ret;
    benign felicitations,
    neopagan pantheist

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    If NULL / 0 is always invalid as a pointer/id/handle, then assigning these at the start can be useful.
    Code:
        context = 0;
        command_queue = 0;
        program = 0;
    
        context = clCreateContext(NULL, 1, &device_id, NULL, NULL, NULL);
        if (err != CL_SUCCESS) {
            printf("unable to create context. ERROR CODE: %i\n", err);
            context = 0; // in case the API call doesn't return a consistent invalid value
            goto err;
        }
    
    
        // create command queue
        command_queue = clCreateCommandQueue(context, device_id, 0, &err);
        if (err != CL_SUCCESS) {
            printf("unable to create command queue\n");
            command_queue = 0; // in case the API call doesn't return a consistent invalid value
            goto err;
        }
    
        // create program object
        program = clCreateProgramWithSource(context, 1, &ProgramSource, NULL, &err);
        if (err != CL_SUCCESS) {
            printf("unable to create program\n");
            program = 0; // in case the API call doesn't return a consistent invalid value
            goto err;
        }
    
    ....
        return 0;
    
    err:
        if ( program ) clReleaseProgram(program);
        if ( command_queue ) clReleaseCommandQueue(command_queue);
        if ( context ) clReleaseContext(context);
        return 1;
    }
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User
    Join Date
    Sep 2016
    Posts
    2
    thanks for your comments

    so in that case it seems not to be as 'ill-reputed' to use a goto as it sometimes is
    i think i like that approach most, since it also looks clean and readable to me.




    In general when the program exits, all memory will be reclaimed by the OS, so it's typically not something you need to worry about.

    as far as i understood this should be the case on most 'common' os, but that is not for sure on every embedded system right?


    thanks again to all of you

  6. #6
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    > so in that case it seems not to be as 'ill-reputed' to use a goto as it sometimes is

    And that's the problem with "XYZ is evil!" arguments, it closes minds to a feature when it's reasonable or even the best option. I recall getting blasted here for using goto for flow control once, even though it was objectively the cleanest and most efficient approach.

    > as far as i understood this should be the case on most 'common' os, but that is not for sure on every embedded system right?

    Correct. That said, there are two leak situations that are different but ultimately have the same best practice:

    • Leaking memory in a long running program.
    • Leaking memory if the OS doesn't reclaim it on termination.

    In both cases you clean up after yourself by releasing memory when it's no longer needed. The first case is your primary concern because it's more commonly an issue, and the second case is more for maximizing portability.

    That said, one thing to note is if you're on a heavily restricted embedded environment, use of dynamic memory at all might be prohibitively expensive. The rules tend to change when you go embedded.
    My best code is written with the delete key.

  7. #7
    Registered User rstanley's Avatar
    Join Date
    Jun 2014
    Location
    New York, NY
    Posts
    1,111
    Quote Originally Posted by indoet93 View Post
    Terima kasih untuk artikel
    We don't understand what you said. Please post responses, and comments in English.

    Thank you.

  8. #8
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Quote Originally Posted by indoet93 View Post
    Terima kasih untuk artikel
    Kamu bodoh

  9. #9
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    Quote Originally Posted by algorism View Post
    Kamu bodoh
    I miss the like button.
    What can this strange device be?
    When I touch it, it gives forth a sound
    It's got wires that vibrate and give music
    What can this thing be that I found?

  10. #10
    Registered User rstanley's Avatar
    Join Date
    Jun 2014
    Location
    New York, NY
    Posts
    1,111
    Quote Originally Posted by Elkvis View Post
    I miss the like button.
    Why was the "Like" button removed?

  11. #11
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    Quote Originally Posted by Prelude View Post
    > so in that case it seems not to be as 'ill-reputed' to use a goto as it sometimes is

    And that's the problem with "XYZ is evil!" arguments, it closes minds to a feature when it's reasonable or even the best option. I recall getting blasted here for using goto for flow control once, even though it was objectively the cleanest and most efficient approach.

    > as far as i understood this should be the case on most 'common' os, but that is not for sure on every embedded system right?

    Correct. That said, there are two leak situations that are different but ultimately have the same best practice:

    • Leaking memory in a long running program.
    • Leaking memory if the OS doesn't reclaim it on termination.

    In both cases you clean up after yourself by releasing memory when it's no longer needed. The first case is your primary concern because it's more commonly an issue, and the second case is more for maximizing portability.

    That said, one thing to note is if you're on a heavily restricted embedded environment, use of dynamic memory at all might be prohibitively expensive. The rules tend to change when you go embedded.
    I agree about the "XYZ is evil!" dogma. Absolutely unproductive and can make worse software. It's unfortunate that it's so prevalent across the entire industry too. No matter the language, the paradigm, the company, someone will not doing something "just because".

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Do I need to clean up this memory?
    By EverydayDiesel in forum C++ Programming
    Replies: 1
    Last Post: 10-31-2015, 12:07 AM
  2. Catching user errors
    By cda67 in forum C Programming
    Replies: 6
    Last Post: 11-22-2011, 06:24 PM
  3. loads of linker errors, and I only tried to clean up D:
    By Akkernight in forum C++ Programming
    Replies: 6
    Last Post: 02-23-2009, 12:15 PM
  4. How to Restart or Clean all Variable in memory?
    By sergioms in forum C Programming
    Replies: 5
    Last Post: 01-08-2009, 01:59 PM
  5. MSVC 05 standard not catching memory leaks
    By VirtualAce in forum C++ Programming
    Replies: 0
    Last Post: 12-27-2007, 05:50 PM

Tags for this Thread