Created
July 20, 2011 18:27
-
-
Save niuk/1095566 to your computer and use it in GitHub Desktop.
Resource Handling, PART I
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Say you want to read some stuff into a buffer. | |
* To do so in C, a (very) naive programmer would write something like: */ | |
void *read_buf_from_file(char *path) { | |
void *buf = malloc(512); | |
int fd = open(path, O_RDONLY); | |
read(fd, buf, 512); | |
return buf; | |
} | |
/* That is very bad code. It doesn't do any error checking, so | |
* if any one of malloc, open or read failed, you're boned. Also, | |
* it leaks the opened file descriptor so that needs to be rectified. | |
* Thus, C programmers usually dutifully wrap their library calls in | |
* error checking conditionals, like so: */ | |
void *read_buf_from_file(char *path) { | |
void *buf = malloc(512); | |
if (buf == NULL) return NULL; | |
int fd = open(path, O_RDONLY); | |
if (fd < 0) return NULL; | |
if (read(fd, buf, 512) < 0) return NULL; | |
close(fd); | |
return buf; | |
} | |
/* Slightly better, but still problematic. The first return is okay. | |
* The second one would result in a memory leak, and the third one would | |
* cause both a memory leak and a leaked file descriptor. Thus, C programmers | |
* also have to make sure every resource is freed even in the case of an error: */ | |
void *read_buf_from_file(char *path) { | |
void *buf = malloc(512); | |
if (buf == NULL) return NULL; | |
int fd = open(path, O_RDONLY); | |
if (fd < 0) { | |
free(buf); | |
return NULL; | |
} | |
if (read(fd, buf, 512) < 0) { | |
free(buf); | |
close(fd); | |
return NULL; | |
} | |
close(fd); | |
return buf; | |
} | |
/* This code pretty much handles every case (except one, but | |
* we'll get to that later). So now the main problem is aesthetics. | |
* Note that both "free(buf)" and "close(fd)" are each written twice. | |
* If the number of resources increases, they might be repeated many, | |
* many times. Repeating yourself is always bad and almost always avoidable. | |
* Another, better approach would be: */ | |
void *read_buf_from_file(char *path) { | |
void *buf = NULL; | |
int fd = open(path, O_RDONLY); | |
if (fd < 0) goto e0; | |
buf = malloc(512); | |
if (buf == NULL) goto e1; | |
if (read(fd, buf, 512) < 0) goto e2; | |
goto e1; | |
e2: free(buf); | |
buf = NULL; | |
e1: close(fd); | |
e0: return buf; | |
} | |
/* This kind of code, despite the use of gotos (which are Considered Harmful) | |
* is actually quite idiomatic in some settings. The resource management code | |
* is not duplicated because there is only one exit point for the function. | |
* Furthermore, the resources are decallocated in the reverse order of their | |
* allocation, which is desirable for cases where dependencies need to be | |
* observed. However, gotos are somewhat distasteful, and this code would | |
* stop working if we introduce exceptions, which can cause functions to exit | |
* at pretty much any point. Imagine that open, malloc and read throw | |
* exceptions instead of returning an error value: */ | |
void *read_buf_from_file(char *path) { | |
void *buf = malloc(512); | |
// If malloc throws an exception, we're still okay. | |
int fd = open(path, O_RDONLY); | |
// If open throws an exception, we got a memory leak. | |
read(fd, buf, 512); | |
// If read throws an exception, we have both the memory leak | |
// and a file descriptor leak. | |
return buf; | |
} | |
/* In this imaginary language, there is no clear way to avoid the leaks, | |
* except by using try/catch. But to handle every possible error case, the | |
* function has to catch every possible exception, which kind of defeats the | |
* point of having exceptions in the first place. Furthermore, the code needed | |
* for this would be so verbose, I'm not even gonna write it out. */ | |
/* C++ solves this problem by having destructors on objects that get called | |
* every time the object goes out of scope. Destructors are extremely useful, | |
* but they require wrapping resources in classes, which is itself a verbose | |
* affair. The code above would look something like the following: */ | |
class Buffer { | |
void *buf; | |
... | |
} | |
class File { | |
int fd; | |
... | |
} | |
void *read_buf_from_file(char *path) { | |
Buffer buf = new Buffer(512); | |
File fd = new File(path, File::READONLY); | |
File.read(buf, 512); | |
return buf; | |
} | |
/* Very pretty, if we ignore the wrapper classes needed. However, there is | |
* a problem we need to address in File's destructor: */ | |
File::~File() { | |
if (close(fd) < 0) { | |
throw new FileCloseException(); | |
} | |
} | |
/* The problem is that destructors are never supposed to throw exceptions. | |
* If the destructor was itself triggered by an exception, then throwing a | |
* new one would cause the runtime to lose one of the two pieces of information. | |
* Both of them, however, are needed for the program and its programmer to know | |
* what the hell went on. Currently, no language solves this problem elegantly. */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment