Search code examples
rubymultithreadingio

Why is Ruby's IO#pwrite thread-safe?


I was wondering if someone could explain me why Ruby's IO::pwrite function is said to be thread-safe in the documentation:

This is advantageous to combining IO#seek and IO#write in that it is atomic, allowing multiple threads/process to share the same IO object for reading the file at various location

My understanding of atomicity is that it's all or nothing, if an error is raised the "transaction" will be rolled back so in this case the file would be closed with its original contents (correct?).
Atomicity does not guarantee thread synchronization however, unless rb_thread_io_blocking_region is a synchronized method?
Here's a snippet of the source of the pwrite function, also available here

    n = (ssize_t)rb_thread_io_blocking_region(internal_pwrite_func, &arg, fptr->fd);
    if (n < 0) rb_sys_fail_path(fptr->pathv);
    rb_str_tmp_frozen_release(str, tmp);

    return SSIZET2NUM(n);
}

Solution

  • The synchronization is performed by the kernel (the operating system), not Ruby.

    As per the documentation, Ruby's pwrite calls this pwrite which takes care of the synchronization.

    The behavior of pwrite system call is described here. Specifically:

    After a write() to a regular file has successfully returned:

    • Any successful read() from each byte position in the file that was modified by that write shall return the data specified by the write() for that position until such byte positions are again modified.

    • Any subsequent successful write() to the same byte position in the file shall overwrite that file data.

    The extensive rationale discusses serialization in more detail.