The Dreadful ‘Goto’

The ‘goto’ operation has a very bad reputation when it comes to high-level programming languages. I have heard many times the sentence “never ever use goto”. I think that the fear of ‘goto’ is generally justified since it can lead to horrible spaghetti code. If really abused, it can completely corrupt the structure of a program, making it potentially impossible to track its logic.
However, I would not go as far as removing it completely from our toolbox. In general, I am careful when I hear those “never use” declarations. The root to goto’s destrcutiveness is actually its overwhelming power – it can be used to jump from anywhere to everywhere in the program (with limits, going really wild is restricted by various languages), breaking through walls, leaving destruction in its wake. Well, if it is so powerful, is it possible that there is good use to it, sometimes?

Exceptions to the “goto embargo”

One use of ‘goto’ that I personally find useful, is for resources release at the end of functions. I have seen this practice used in production and Open Source code so I think it may have become a common practice to some degree. Let me first present the underlying challenge.

The Challenge

Personally, I find the “one-time do-while” practice a good and clean way to write C code that properly handles errors. It looks like this:

int foo()

{

   int status; 

 

   do

   {

      status = bar();

      if(0 != status)

      {

         break;

      }

 

      status = baz();

      if(0 != status)

      {

         break;

      }

 

      //And so on

 

   }while(0);

 

   return status;

}

 

Now assume that you have a function that includes several resource allocations. In a perfect world you would do the allocations, use them, and at the end you will release all the resources. In practice, each of the allocations can fail, and you should use something like the above in order to properly address those possible failures. However that may prove difficult since you may be looking at a variable amount of release operations.
Let’s modify the above example to do allocations that require a release, with an example. The example is a  horrible way to handle the releases, and is presented just for illustration:

 

int foo1()

{

       //Init to success, change if an error occurs.

       //**NOTE**: from security perspective it is better to init to “error”

       //          and change it to “success” if all is good. The focus here

       //          is to simplify the example.

       int status = 0;

 

       do

       {

             int * p1 = (int*)malloc(8);

             if(NULL == p1)

             {

                    status = -1;

                    break;

             }

 

             int * p2 = (int*)malloc(8);

             if(NULL != p2)

             {

                    // This allocation failed but the previous

                    // one didn’t, we must free it

                    free(p1);

                    status = -2;

                    break;

             }

 

             int * p3 = (int*)malloc(8);

             if(NULL != p3)

             {

                    //Now we must free the first…

                    free(p1);

                    //and the second allocation!

                    free(p2);

                    status = -3;

                    break;

             }

 

             //And so on

 

             //Here something is done with the resources

 

       }while(0);

 

       return status;

}

 

This is of course highly error-prone, not scalable, not maintainable and not recommended. Every change somewhere in the function may require a change in multiple other places. Moreover, the code is inflated with repetitive code. At least for this code, there is a better option:

int foo2()

{

       int * p1;

       int * p2;

       int * p3;

 

       //Init to success, change if an error occurs.

       int status = 0;

       do

       {

             p1 = (int*)malloc(8);

             if(NULL == p1)

             {

                    status = -1;

                    break;

             }

 

             p2 = (int*)malloc(8);

             if(NULL == p2)

             {

                    status = -2;

                    break;

             }

 

             p3 = (int*)malloc(8);

             if(NULL == p3)

             {

                    status = -3;

                    break;

             }

 

             //And so on

 

             //Here something is done with the resources

 

       }while(0);

 

       //Release memory that was successfully allocated

       if(NULL != p1) free (p1);

       if(NULL != p2) free (p2);

       if(NULL != p3) free (p3);

       //And so on

 

       return status;

}

This is nice enough when all your allocations are mallocs, but that is not the only option for resource allocation. Resources can be syncronization entities, HW resources, File System resources (e.g. file open) and so on, and not all will have a way of detecting the success of the allocation operation that is as nice as a simple comparison to NULL.

Reverse release using ‘goto’

Let’s modify the above function to do arbitrary resource allocations. Also, we will create a ‘release block’ at the bottom of the function that will arrange the release operations in an order reversed to the allocation order. This will allow us to jump into the block and execute only the appropriate release operations:

int foo3()

{

       //Init to success, changeif an error occur.

       int status = 0;

 

       HANDLE handle1 = allocate_resource1();

       if(INVALID_HANDLE == handle1)

       {

             status = -1;

             goto release_resource1;

       }

 

       HANDLE handle2 = allocate_resource2();

       if(INVALID_HANDLE == handle2)

       {

             status = -2;

             goto release_resource2;

       }

 

       HANDLE handle3 = allocate_resource3();

       if(INVALID_HANDLE == handle3)

       {

             status = -3;

             goto release_resource3;

       }

 

       //And so on

 

       //Here something is done with the resources

 

       //Release block (release in a reversed order)

release_resource3:

       deallocate_resource3();

release_resource2:

       deallocate_resource2();

release_resource1:

       deallocate_resource1();

 

       //And so on

 

       return status;

}

Further benefits

Another interseting benefit of the ‘goto’ error handling method over the “do-while” method is its robustness (credit: Avishai Gantz). The ‘break’ expression is used in a context and can only break out of the scope in which it is declared in, not further. ‘goto’ on the other hand can break a faulty flow and take it directly into the release block without further complications.
Consider the following example, in which a ‘while’ loop has to be used in the function’s flow, while the above “do-while” error-handling method is being used:

int foo()

{

       int status = 0;

       int * pointer_array[MAX_ELEMENTS] = {NULL};

       do

       {

             status = bar();

             if(0 != status)

             {

                    break;

             }

            

             .

             .

             .

            

             uint32_t counter = 0;

             //For clarity I have omitted required checks

             //on out-of-range return values

             uint32_t boundary = number_of_elements();

             while(counter < boundary)

             {

                    pointer_array[counter] = malloc(sizeof(ELEMENT));

                    if(NULL == pointer_array[counter] )

                    {

                           status = -1;

                           //BUG! – this will break to the outside of *this*                                //       while loop, not out of the external, 

                           //       error-handling one

                           break;

                    }

             };

            

             status = baz()

             if(0 != status)

             {

                    break;

             }

            

             .

             .

             .

            

       }while(0);

 

 

       return status;

}

 

The above example illustrates the scope issue of ‘break’. This cannot happen if you use the ‘goto error block’ method.
This bug really happened in prodcution code. It is unclear if the code was written like this, or if that is a result of a refatoring operartion with the intent to improve error handling while not noticing the implications of already having a while loop there.

Handle With Care

In my opinion, beyond being a rare example of when goto can (or should) be used, the example above illustrates what are the characteristics of possible other examples:

  • goto jumps inside the confines of the smallest possible scope
  • goto Jumps only downwards
  • All the goto’s are aimed at a specific, targeted location, not all around
  • goto is used in a consistent and methodological way, constituting a clear method. If you have many of those error-handling structures in the code, the reader knows exactly where this is going after reading just few of them

So, the bottom line in my opinion is still definitely “Handle With Care”, but not “Never Use”.

One Comment

Leave a Comment

Your email address will not be published. Required fields are marked *