Originally Posted by
slavik
should malloc be a blocking system call?
Absolutely not (well, not unless there's a way to disable blocking. And even then, it should be off by default). Being able to allocate memory is probably the most basic function that an app needs to do. And this may indeed be done in a situation where some important process needs to complete. So, malloc should not block an app. If malloc can't fullfill the request, it should "immediately" return 0, and not terminate the app, and let the app decide how it wants to proceed.
Note I put "immediately" in parentheses. This is because, typically malloc is a very time-consuming call. It takes awhile for it to complete. And note that it's almost a guarantee that, on a multi-tasking OS, malloc will have some sort of "memory resource arbitration". After all, malloc somehow has to manage a list of available free memory, and you can't have two threads simultaneously monkeying with that list. That's like having two cooks making one cake, blindfolding them, and giving them both the same set of ingredients. They're probably going to mess up each other's actions. So malloc itself may indeed "serialize" access to the list. In other words, it may indeed block your app. But it should ideally do so for the shortest possible amount of time, and absolutely not indefinitely. (I'm trying to explain this in a really simple, clear way. It's very complicated).
If I need 100MB of memory and can't do my function if it isn't there, what should I/OS do if there isn't 100MB free that I can use?
The OS (essentially, malloc) should do exactly what I describe above -- not terminate you, and instead return 0 immediately.
You should do whatever you think is the best way to recover. For example, maybe plan ahead. I'll give you an anecdote to illustrate.
I once saw this error handling function in a shared lib. The function took an error number, and it returned a nul-terminated error message (for that error number). For example, if you passed an error number of 5, it would return the nul-terminated string "Out of memory". This is so that the app could display an error message to the enduser.
Now, the guy decided that he wanted to have error messages in various languages (ie, english, spanish, republican, democrat, etc). So he put all of the english messages in one text file. Then, he put all of the corresponding spanish messages in another text file. He used the computer's locale to decide which text file to reference.
This isn't the actual code, but let's say it was something like this. (NOTE: I'm showing half-finished untested code without error-checking, just to move things along):
Code:
char * get_error_message(int error)
{
FILE *fh;
char *str;
char buf[256];
// Open the text file
fh = fopen(MyTextFilename, "r");
str = 0;
// Go through the lines, to find the line corresponding
// to the error number
do
{
str = fgets(buf, sizeof(buf), fh);
} while (error-- && str);
// Make a copy of the string
if (str)
{
char *temp;
temp = malloc(strlen(str) + 1);
// NOTE: Here the Pulse Audio author would call
// exit()... oh excuse me, _exit()... if temp == 0.
strcpy(temp, str);
str = temp;
}
fclose(fh);
// Return error msg to app. NOTE: App must free()
// it when done
return(str);
}
Now there's a potential problem with regard to malloc. What if the call fails? The function returns a 0 which means the app has no error message to display. Not too useful. (In fact, it's downright funny. If the app passes a 5, and malloc fails, that means the function failed to return the message "Out of memory" because... um... it ran out of memory. I laughed. I thought it was a whole lot funnier than pmasiar thought it was when the Pulse Audio author said that it doesn't matter whether you handle malloc failing).
I thought the best solution was to allocate memory, right at the start of the program, to load the text file into an array of nul-terminated strings (with an extra nul at the end). Then I rewrote his function (again not actual code):
Code:
char * get_error_message(int error)
{
char *str;
// Get the array already in memory
str = ArrayOfNulTermStrs;
// Go through the lines, to find the line corresponding
// to the error number
while (error-- && *str)
str += strlen(str) + 1;
// Return error msg to app. NOTE: App must NOT free()
// it when done, nor alter it at all. Just display it as is
return(str);
}
So I planned ahead by eliminating the malloc where it was very inconvenient to have it. If I couldn't alloc the mem when the app started up, well at least the user was informed of that before he started working. He didn't get to a situation where something failed, and yet he couldn't be informed what happened.
Note that an easy way of fixing it could have been to alter the malloc'ing:
Code:
temp = malloc(strlen(str) + 1);
if (temp)
{
strcpy(temp, str);
str = temp;
}
else
str = "Please close other running programs and retry the operation";
Ok, maybe he doesn't get the error message he was supposed to get. And it's in english which is maybe not the right language. But it's better than nothing at all, right? If the enduser follows the advice, and it works, he should be happy. And if he's happy, then you accomplished your job. Sometimes, posting an error message like the above is perfectly fine (assuming your app isn't like some heart monitor software. You have to design your program with the usage in mind).
But please, please, please don't stick a call to exit() (or _exit()) in there when malloc fails, even if the Pulse Audio author tells you (and pmasiar agrees) it's ok because your app "isn't important", and maybe the OS will terminate you anyway, and the enduser didn't need to save his work anyway. Please, please, please, folks. Just don't. Your endusers will thank you.
Bookmarks