Many functions require the allocation of multiple resources. Failing and returning somewhere in the middle of this function without freeing all of the allocated resources could produce a memory leak. It is a common error to forget to free one (or all) of the resources in this manner, so a goto - chain is the simplest and cleanest way to organize exits while preserving the order of freed resources.
...
In this noncompliant example, exit code is written for every instance in which the function can terminate prematurely. Notice how failing to close fin2
produces a resource leak, leaving an open file descriptor.
_Please note that these examples assume {{ Wiki Markup errno_t
}} and {{NOERR
}} to be defined, as requested by \[[guideline DCL09-C. Declare functions that return an errno error code with a return type of errno_t|DCL09-C. Declare functions that return errno with a return type of errno_t]\]. An equivalent compatible example would define {{errno_t
}} as an {{int
}} and {{NOERR
}} as zero._
Code Block | ||
---|---|---|
| ||
typedef struct object { // A generic struct -- The contents don't matter int propertyA, propertyB, propertyC; } object_t; errno_t do_something(void){ FILE *fin1, *fin2; object_t *obj; errno_t ret_val; fin1 = fopen("some_file", "r"); if (fin1 == NULL) { return errno; } fin2 = fopen("some_other_file", "r"); if (fin2 == NULL) { fclose(fin1); return errno; } obj = malloc(sizeof(object_t)); if (obj == NULL) { ret_val = errno; fclose(fin1); return ret_val; // forgot to close fin2 !! } // ... more code ... fclose(fin1); fclose(fin2); free(obj); return NOERR; } |
This is also just a small example; in much larger examples, errors like this would be even harder to detect.
...
In this revised version, we have used a goto - chain in replacement of each individual return segment. If there is no error, control flow will fall through to the SUCCESS
label, release all of the resources, and return NOERR
. In the case of an error, the return value will be set to errno
, control flow will jump to the proper failure label, and the appropriate resources will be released before returning.
Code Block | ||
---|---|---|
| ||
// ... assume the same struct as above ... errno_t do_something(void) { FILE *fin1, *fin2; object_t *obj; errno_t ret_val = NOERR; // Initially assume a successful return value fin1 = fopen("some_file", "r"); if (fin == NULL) { ret_val = errno; goto FAIL_FIN1; } fin2 = fopen("some_other_file", "r"); if (fin2 == NULL) { ret_val = errno; goto FAIL_FIN2; } obj = malloc(sizeof(object_t)); if (obj == NULL) { ret_val = errno; goto FAIL_OBJ; } // ... more code ... SUCCESS: // Clean up everything free(obj); FAIL_OBJ: // Otherwise, close only the resources we opened fclose(fin2); FAIL_FIN2: fclose(fin1); FAIL_FIN1: return ret_val; } |
The benefits of this method are that This method is beneficial because the code is cleaner, and we prevent the rewriting of do not need to rewrite similar code upon every function error.
...
Some effective examples of goto - chains are quite large. The following compliant code is an excerpt from the Linux kernel. This is the copy_process
function from kernel/fork.c
from Version 2.6.29 of the kernel.
The function uses 17 goto labels (not all displayed here) in order to perform cleanup code should any internal function yield an error code. If no errors occur, the program returns a pointer to the new process p
. If any error occurs, the program diverts control to a particular goto label, which performs cleanup for the sections of the function that have currently been successfully executed, while not performing cleanup on sections of the function that have not been executed yet. Consequently, only resources that were successfully opened are actually closed.
...
Wiki Markup |
---|
\[[ISO/IEC 9899:1999|AA. Bibliography#ISO/IEC 9899-1999]\] Section 7.20.3, "Memory management functions" \[[ISO/IEC 9899:1999|AA. Bibliography#ISO/IEC 9899-1999]\] Section 7.19.5, "File access functions" [Linux kernel Sourcecode (v2.6.xx)|http://www.kernel.org/pub/linux/kernel/v2.6] 2.6.29, {{kernel/fork.c}}, the {{copy_process()}} function \[[Seacord 052005|AA. Bibliography#Seacord 05]\] Chapter 4, "Dynamic Memory Management" |
...