help-bison
[Top][All Lists]
Advanced

[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





reply via email to

[Prev in Thread] Current Thread [Next in Thread]