task.c: fflush before fork to avoid duplicated output buffers (fix: #67)#68
task.c: fflush before fork to avoid duplicated output buffers (fix: #67)#68oreo639 wants to merge 1 commit intorrthomas:masterfrom
Conversation
|
Thanks very much for this! What's the reason for flushing all streams here, and not just the ones we write to? If we can just flush those (in fact, just the one output stream), then we should be safe, since we never |
My concern was that process exit also flushes the streams. e.g. if you do a printf before calling recode and that printf is buffered, then the output will be duplicated when both processes exit: (Outputs If you do: The output is also duplicated even though fclose isn't explicitly called. If that is something consumers just need to be wary of, then I can make it only flush the output streams passed into and created by recode. (and maybe stdout?) |
|
Thanks for the explanation. So it seems that the options are:
I have to say, I'm tempted to take the last option. In theory, of course, forking can improve performance, but this is quite an annoying potential gotcha. I looked into closing all file handles we don't care about, but I don't think that will work either, as we can only identify and close them at the file descriptor level, which will presumably just confuse libc and cause a crash. To do this cleanly librecode would have to work with file descriptors rather than streams, and then it wouldn't have the buffering problem in the first place. |
|
Remove the pipe code path in preference to attempting to fix this issue, which as outlined above has subtle and annoying implications for the caller that are basically impossible to fix. |
|
Thanks. |
Not entirely sure how to make a test for this with the python bindings.
When a process is forked, all of the memory is duplicated in the new process, this also includes the
FILE*buffers, which can result in those buffers being flushed multiple times.To prevent this, output buffers can be flushed before forking.
One potential issue with this approach though,
fflush(NULL)can interfere with calls toungetc(), in which casefflush(NULL)will result in that being undone. (although on linux, FreeBSD, Windows, and probably othersfflush(NULL)is defined as only applying to output streams, so it wouldn't affectungetc()on stdin or streams opened in read mode)