3

Here is process a and b, both of which are multithreaded.

  1. a forks b and b immediatly execs one new program;
  2. a dups and freopens stderr to the logfile (a is defacto apache's httpd2.22)
  3. b inherits the opened stderr from a. ( i am adapting apache httpd, b is my program), and b uses fprintf(stderr....) for logging
  4. so a, b share the same file for logging
  5. there is no lock mechanism for a, b to write log

I found that some log msg are interleaving, and a little bit of log msg got lost.

Can the two writers to the same file implicitly lock each other out?

The more important question is: If we use fprintf only within one single multithreaded process, fprintf is thread safe, i.e. one call of fprintf won't intervene another fprintf call in another thread? Many articles said this, but this is not easy to ensure myself, so I ask for help here.

A: the code for duplicate the fd is like this:

......
rv = apr_file_dup2(stderr_log, s_main->error_log, stderr_p);//dup the stderr to the logfile
apr_file_close(s_main->error_log);//here ,2 fd point to the same file description,so close one of 

then

B:apache it self use this manner for logging:

......
if (rv != APR_SUCCESS) {
    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s_main, ".........");

C:for convenience,i logging in this way:

fprintf(stderr,".....\n")

I am quite sure apache and me use the same fd for file writing.

Felix Frank
  • 8,125
  • 1
  • 23
  • 30
basketballnewbie
  • 1,713
  • 3
  • 15
  • 18

1 Answers1

9

If you're using a single FILE object to perform output on an open file, then whole fprintf calls on that FILE will be atomic, i.e. lock is held on the FILE for the duration of the fprintf call. Since a FILE is local to a single process's address space, this setup is only possible in multi-threaded applications; it does not apply to multi-process setups where several different processes are accessing separate FILE objects referring to the same underlying open file. Even though you're using fprintf here, each process has its own FILE it can lock and unlock without the others seeing the changes, so writes can end up interleaved. There are several ways to prevent this from happening:

  1. Allocate a synchronization object (e.g. a process-shared semaphore or mutex) in shared memory and make each process obtain the lock before writing to the file (so only one process can write at a time); OR

  2. Use filesystem-level advisory locking, e.g. fcntl locks or the (non-POSIX) BSD flock interface; OR

  3. Instead of writing directly to the log file, write to a pipe that another process will feed into the log file. Writes to a pipe are guaranteed (by POSIX) to be atomic as long as they are smaller than PIPE_BUF bytes long. You cannot use fprintf in this case (since it might perform multiple underlying write operations), but you could use snprintf to a PIPE_BUF-sized buffer followed by write.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • one pipe can only has one end for write and one end for read.so,one pipe can only be used between 2 process.if i set up a dedicated process for logging,and then more then 2 process for working,each of these working process will have to create a pipe and then pass log msg to the logging process through this pipe. so ,these pipes are independent of each other, even without POSIX's guarante,the content in these pipe won't be interleaved by other working process.And it is the logging process's duty to guarantee the msgs it recieved from many pipes not be interleaved-----------am i right?tks – basketballnewbie Jul 27 '12 at 07:21
  • No, you can use the same pipe for all of them just fine. – R.. GitHub STOP HELPING ICE Jul 27 '12 at 12:11
  • tks,i will try this:copy the fd for the write end of the pipe to several process,and the logging process hold the read end. – basketballnewbie Jul 28 '12 at 01:13
  • How about using ap_log_error() in an apache module when using a forking apache setup? Will the logs be interleaved using this function, or does this function prevent that? – Raffi Khatchadourian Apr 23 '14 at 20:24
  • Option #3 is pointless. If you use `snprintf` to synthesize your message in a buffer first, you can use `write` system call on an `O_APPEND` file descriptor directly. No need for pipes or other coordinating processes. – Yakov Galka Apr 17 '20 at 13:44
  • @ybungalobill: nope. write can return a value less than nbytes (short write). – R.. GitHub STOP HELPING ICE Apr 17 '20 at 14:59
  • @R..GitHubSTOPHELPINGICE: not when you're writing to a regular file: https://stackoverflow.com/a/35256561/277176 – Yakov Galka Apr 17 '20 at 18:06
  • @ybungalobill: I believe you (and that answer) that the standard requires (and the mentioned OS's comply with) atomicity of the operation that takes place, but I don't see where it disallows a successful short write. – R.. GitHub STOP HELPING ICE Apr 17 '20 at 18:18
  • @R..GitHubSTOPHELPINGICE: to my understanding it does not prohibit short writes in case of failure. So if you write 1000 bytes you may get only 100 bytes written, but the file pointer will be incremented atomically by 1000 bytes and therefore the writes won't interleave, it's just that one of the log entries will contain junk. That, obviously, applies to any solution that uses O_APPEND. – Yakov Galka Apr 17 '20 at 18:34
  • @ybungalobill: It most certainly can't increment the file position by 1000 bytes but only write 100 bytes and return 100. That would break all existing code that properly handles short writes by restarting with the remaining length, in particular short writes due to arrival of a signal after some data is written but before the full length is written. (This is often not an issue on regular files that do non-interruptible sleep, deferring signals, but it's an issue for all other types and on NFS mounted with interruptible option.) – R.. GitHub STOP HELPING ICE Apr 17 '20 at 18:37
  • If an actual error (like IO error due to failing disk) occurs and messes things up once the change in file position is already committed, the only possible return value is -1 with errno set, not a short write, since the state of the file does not match what it would on a successful short write. – R.. GitHub STOP HELPING ICE Apr 17 '20 at 18:39