help-make
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Questions about signal handling in GNU Make


From: Masahiro Yamada
Subject: Re: Questions about signal handling in GNU Make
Date: Sat, 24 Jul 2021 22:21:47 +0900

On Sat, Jul 24, 2021 at 6:40 AM Kaz Kylheku (gmake)
<729-670-0061@kylheku.com> wrote:
>
> On 2021-07-23 14:19, Kaz Kylheku (gmake) wrote:
> > Secondly, build tools which open a file more than once
> > should not specify O_CREAT for the second and subsequent
> > times. The second and subsequent opens should expect
> > the file to exist, and fail otherwise.
>
> Unfortunately, this is too naive, I apologize
> for writing in haste.
>
> A legitimate strategy for a tool is to write output
> to a temporary file which is then renamed to the
> destination as a last step.
>
> Make, upon processing the interrupt signal, sees that
> there is no file at all, nothing to delete.
>
> Meanwhile, the tool reacts to the signal later, or
> not at all, and executes through the rename system
> call which creates the file.
>
> There is also a race condition between make and the
> very first open of the file, like this script
>
>    #!/bin/sh
>    sleep 5
>    echo "hello" > $1
>
> If this keeps executing while make processes the interrupt,
> then we have the problem, even though there is just
> a single open and write.


Yes, I think this issue happens with a single open and write.

Now we are clear enough, but just in case,
see the following C code example below:




#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
        int fd;
        ssize_t ret;
        char c = 'a';

        if (argc != 2) {
               fprintf(stderr, "please pass the output filename\n");
               exit(1);
        }

        /* [A]: Make processes INT here */

        fd = open(argv[1], O_CREAT | O_WRONLY | O_TRUNC, 0666);
        if (fd == -1) {
                 perror("open");
                 exit(1);
        }

        /* [B]: This tool receives INT here. */

        ret = write(fd, &c, 1);
        if (ret != 1) {
                perror("write");
                exit(1);
        }

        return 0;
}


If GNU Make receives the INT signal at [A],
there is nothing to do since the tool has not
created the target file yet.

Then, if the tool receives the INT signal at [B],
it is terminated. The target has been created
with the updated timestamp, but it is incomplete.




To avoid the race on the tool side, as you mentioned,
we can write to a tmpfile, and rename it as a last step.

But, it is not realistic to require all developers in
a project to follow this rule.

That's why I believe make's automatic cleanup
is an important feature.



>
> The correct behavior is to defer temporary file cleanup
> of files until after no part of the job is running.
>
> Or, perhaps to to it twice: once upon receipt of the
> interrupt signal (because that might be the last
> chance: make could be forcibly terminated after that;
> it's kind of a "best effort").
>
> Then, check those files again and re-delete any that
> have re-appeared after the child jobs have been reaped.


Yes. We can do it twice, but the second one is more
important.

Of course, it is not possible to make it work perfectly.
For example, there is no way to cleanup the target files
when gnu make is killed by 'kill -KILL <PID>'.

So, it is a best effort after all, but I think it can
be improved.




--
Best Regards
Masahiro Yamada



reply via email to

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