SafeStream stream wrapper not atomic

Notice: This thread is very old.
Member | 1

Looking at the source code to SafeStream , it's clear that the implemented code is not Atomic as it claims to be. It does provide additional Isolation , so it's not “bad” as implemented, but it does not provide any checks for atomicity. To understand why, take a look at stream_write:

public function stream_write($data)
    $len = strlen($data);
    $res = fwrite($this->handle, $data, $len);

    if ($res !== $len) { // disk full?
        $this->writeError = TRUE;
    return $res;

True atomic writes should not have the possibility of writing impartial data (which this does). Looking at the man page of fwrite() , we can see a number of reasons why fwrite may fail in a partial write:

  • The file is a regular file and an attempt was made to write at or beyond the offset maximum.
  • A signal interrupted the call.
  • An output error occurred.
  • Memory could not be allocated for internal buffers.
  • An attempt is made to write to a full disk.
  • A device error occurred.
  • An attempt is made to write to a closed pipe or FIFO.

To fix, either simply remove the claim of atomic nature of the wrapper, or implement true atomic writes (which would require the use of temp files and renaming to overwrite on completion instead of writing as a stream). One possible solution would be to redirect all of the data to fwrite to an internal buffer, and commit the buffer (flush it) on every call to fwrite or fflush. When you flush it, you would write to a temporary file, and only if that write succeeded, rename the temp file to the one you want to overwrite…

Note that this may involve some hackery to keep the stream pointing to the correct file, but it would be a true atomic write, as opposed to what's implemented now which is an isolated write…

David Grudl
Nette Core | 7769

SafeStream provides isolation (reader waits until writer is finished or vice versa) a fully atomic write to a new (ie non-existent) files using temporary file.

That's right, atomicity & consistency is provided only for new files, so I reimplemented SafeStream to better ensure consistency and atomicity by rule all or nothing for existing files. Now it makes a copy of file, works with this copy and if there is no error, rename the file back. This affects r+, a, a+ modes. Opening file in this modes works now slower due time required to make a copy.