Search code examples
c++filecrt

How does _sopen_s manage multiple writing to same file


I have a C++ application that handles multiple connections and writes to file. When using function _sopen_s with parameter _SH_DENYNO all threads that are working simultaneously are writing in the file and I see no loss of data. Can you tell me how the access to the file is managed by the function so there is no loss of data ?

Kind regards


Solution

  • If you're using write(), or some OS-supplied variant of it, individual write() calls tend to be implemented such that each call to write() is atomic because of the implications of this POSIX statement regarding write():

    On a regular file or other file capable of seeking, the actual writing of data shall proceed from the position in the file indicated by the file offset associated with fildes. Before successful return from write(), the file offset shall be incremented by the number of bytes actually written. On a regular file, if this incremented file offset is greater than the length of the file, the length of the file shall be set to this file offset.

    That's somewhat hard to implement in a way that allows for interleaved data from separate write() calls on the same file descriptor, because if data is interleaved the change in the file offset upon completion wouldn't be equal to the "number of bytes actually written". So that statement could be interpreted as an implied requirement for the atomicity of write() calls on any "regular file or other file capable of seeking".

    Also, there's the explicit POSIX requirement that write() calls to pipes of less than or equal to PIPE_BUF bytes be atomic:

    Atomic/non-atomic: A write is atomic if the whole amount written in one operation is not interleaved with data from any other process. This is useful when there are multiple writers sending data to a single reader. Applications need to know how large a write request can be expected to be performed atomically. This maximum is called {PIPE_BUF}. This volume of POSIX.1-2008 does not say whether write requests for more than {PIPE_BUF} bytes are atomic, but requires that writes of {PIPE_BUF} or fewer bytes shall be atomic.

    Since write() just gets an int for a file descriptor with no other information directly available as to what the file descriptor refers to, the simplest way to implement write() in a way that meets the POSIX requirement for atomic write() to a pipe is to make each write() call atomic.

    So, while there's no requirement for atomicity unless you're writing less than or equal to PIPE_BUF bytes to an actual pipe, write() tends to be implemented atomically for everything.

    Now, that doesn't mean that whatever the file descriptor points to won't break the data up or interleave it with other data. For example, I wouldn't be surprised at all to see interleaved data if two threads were each to try calling one write() operation to stream several GB of data from MPEG files simultaneously to the same TCP socket.

    And you're not actually calling write(). But the underlying implementation is likely shared.