[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Reporting malloc failure in actions
From: |
Christian Schoenebeck |
Subject: |
Re: Reporting malloc failure in actions |
Date: |
Wed, 30 Dec 2020 13:58:54 +0100 |
On Mittwoch, 30. Dezember 2020 07:48:18 CET Akim Demaille wrote:
> > Looking at the generated foo.tab.c file for my parser, I see these
> >
> > macros:
> > #define YYACCEPT goto yyacceptlab
> > #define YYABORT goto yyabortlab
> > #define YYERROR goto yyerrorlab
> >
> > Later in the file I see an interesting label that has no corresponding
> >
> > documented macro:
> > /*-------------------------------------------------.
> >
> > | yyexhaustedlab -- memory exhaustion comes here. |
> >
> > `-------------------------------------------------*/
> >
> > yyexhaustedlab:
> > yyerror (scanner, callback, YY_("memory exhausted"));
> > yyresult = 2;
> > goto yyreturn;
> >
> > This seems to be exactly the code I should execute, since it would
> > inform the caller what went wrong. One option would be for my code to do
> >
> > something like this:
> > if (!(bla = malloc(n)))
> >
> > goto yyexhaustedlab;
> >
> > I don't like relying on undocumented internals like this, since they're
> > subject to change across versions of Bison. Any advice?
>
> I guess most people just fail here, and don't even try to recover from
> memory exhaustion. Out of curiosity: do you really manage to keep your
> program working after you've run out of memory?
Yes, most applications are simply unable to recover from a no memory
situation.
For that reason e.g. g_malloc() and g_malloc0() (unlike malloc()) only return
NULL if the size argument was zero, otherwise they usually print an error and
abort the process if they were unable to allocate the requested (non zero)
size. This allows you to get a core dump and investigate with a debugger how
it ran into that no mem situation as early as possible.
In mission critical server applications, a common design is to rather wrap
sensitive tasks into their own process, and if a process runs into no mem,
letting the process die and if required respawn a new process instead.
Embedded systems with limited code complexity are probably the rare cases
where you might be able to recover the process from no mem without fatality.
> I don't think this area (yyexhaustedlab) would change, but you are right
> it is an internal detail. I'll add something public in the future,
> maybe YYNOMEM. Any idea of a good spelling for such a macro?
> YYEXHAUSTED doesn't sound right.
YYNOMEM sounds more reasonable to me.
When it comes to error handling in Bison/Flex user actions, I would recommend
to always define and use your own macros and/or functions in user actions
instead of calling Bison/Flex ones directly. That safes you refactoring work
over time.
In this case you might replace-wrap your malloc() call by g_malloc0(), or use
something like this if you don't want to use glib:
#define PARSER_ALLOC(x) parser_alloc(x, #x)
static void* parser_alloc(size_t n, const char* expr) {
if (!n) return NULL;
void* p = malloc(n);
if (!p) {
fprintf(stderr, "Parser: malloc(%s) with size %zu failed.
Aborting.\n",
expr, n);
abort();
}
memset(p, 0, n);
return p;
}
...
variable_assignment: VARIABLE '[' expr ']' ASSIGNMENT expr {
const char* varName = $1;
const size_t varSize = $3;
Foo* foo = PARSER_ALLOC(varSize * sizeof(Foo));
...
}
Best regards,
Christian Schoenebeck