Date: Wed, 16 Jun 2021 15:39:55 -0700 From: "Ronald F. Guilmette" <rfg@tristatelogic.com> To: freeBSD Mailing List <freebsd-questions@freebsd.org> Subject: Re: Is a successful call to write(2) atomic? Message-ID: <31698.1623883195@segfault.tristatelogic.com> In-Reply-To: <7801a6e8-2428-619e-5722-7317841595a1@qeng-ho.org>
next in thread | previous in thread | raw e-mail | index | archive | help
In message <7801a6e8-2428-619e-5722-7317841595a1@qeng-ho.org>, Arthur Chance <freebsd@qeng-ho.org> wrote: >>From Posix documentation on write(2): > >Atomic/non-atomic: A write is atomic if the whole amount written in one >operation is not interleaved with data from any other process. > >However, only two cases are guaranteed to be atomic: > >1) writes to a pipe/FIFO of <= PIPE_BUF bytes > >See the beginning of the Rationale section of > >https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html > >2) writes to a regular file from different pthreads *within the same process* Ohhhhhhh! Thank you for fishing all of this info out for me! It certainly puts a different complexion on the whole matter. >Apart from the first case there is no requirement for atomic writes in >Posix when multiple processes are involved. Yes. Got it. Thanks. And to make matters even slightly MORE annoying, here on my FreeBSD 12.2 system it seems that PIPE_BUF is defined in <sys/syslimits.h> to what I consider to be an unfortunately low number... obviously a relic of ancient times, i.e. 512. (Some of my output lines are quite likely to exceed this.) The only reason I mention that is because I've just tried changing my code so that the FD (#1) gets O_APPEND set on it at startup time and then the calls to write() are wrapped in calls to flock() just as John Levine and others suggested. Result? Output is still garbled. Thus, I am off to pursue plan C, i.e. passing completed lines up from the "worker bee" children to the master/parent process which will then be responsible for doing all of the actual writing of lines to FD #1. I had hoped that maybe I could do this simply and just have a single common "up" pipe to carry completed lines from the children to the parent, but because POSIX only assures me that lines up to 512 bytes may be written atomically to that pipe, and because some of my output lines may exceed that, I guess that I have to use separate "up" pipes for -each- child process (and then make calls to select(), within the parent, as was suggested here, and having the master read from whichever ones of those have data available at any given point in time). Anyway, I'll be trying that next. The one really odd thing about all of this is that I already have, and have had, for a long time now, *many* programs that I've built and that I have been using, also for a long time, that follow this same general model, i.e. a parent a a lot of "worker bee" child processes where each of the children, after completing a work item, does itself write a single line of output, and *those* programs have all seemed to work flawlessly (i.e. no output garbling) over a long period of time.... HOWEVER they are all using the stdio functions to write to stdout, rather than calling write() directly to write to FD #1. I only changed *this* one program of mine to call write() directly because I've been perpetually worried that having a lot of independend child processes all writing to their (shared) stdout in a totally uncoordinated and asynchronous manner was just asking for trouble. But now, instead of -avoiding- trouble (by switching from calling stdio functions to making direct calls to write()) I seem to have possibly created trouble for myself instead! Obviously, I have no good explanation for this, but I do think that before I try anything else I may just try changing the child processes in this program so that the child processes call fwrite() rather than write()... just to see if that makes any difference. I can't see any clear reason why using fwrite() rather than write() could, would, or should help, but what do I know? Meanwhile, later that same day... OK, so I tried swapping out calls to write() to call to fwrite()... Result: Some output lines still garbled. So I'm either getting bitten by the non-atomicity of {f}writes or else I've just got a loose pointer in my code somplace. I will be checking both theories and following up here with more results, when and as I have them. Meanwhile, my thanks to everyone who has chimed in. Regards, rfg
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?31698.1623883195>