
Things Unix can do atomically (2010) - turrini
https://rcrowley.org/2010/01/06/things-unix-can-do-atomically.html
======
atrust
In a few simple words, can someone explain what does "atomically" mean? I
personally used this term when talking about some Redis operations, but never
knew the real gist of the word and concepts behind it. I have a very brief
understanding of the term and if I'd have to explain it to a person, I'd say
it's "the operation that does not have any side effects when performing its
unit of work". Is my understanding even close to what atomic operation really
is?

~~~
sp332
Not really. It just means that it's indivisible (the original meaning of
"atom"). Either it succeeds or fails, you never have to worry about it being
half-finished. This includes actions which are so small they are literally
indivisible, or actions which roll back to the original state if they fail.

~~~
atrust
Got it. Now it makes more sense to me. Now I know people tend to talk about
atomicity when it comes to low-level-ish things. But say I create some sort of
a web service with a bunch of business logic. Does it worth to follow this
principle in that case? For instance, client sends an API request (let's say
"Add user to friends"), is it even possible to apply atomicity for these type
of things?

Edit: Thanks everyone for taking time to explain it to me.

~~~
trungaczne
The business logic of your web application should resolve around database
calls. Popular databases should already guarantee these atomicity properties
for you through transactions.

~~~
gnaritas
> The business logic of your web application should resolve around database
> calls

Might, not should. All web apps are not just front ends to a single database
where transactions are useful and once you leave the realm of a single
database into a more distributed type system then transactions are no longer
an option.

------
ridiculous_fish
Here's one that's not super well-known: writes to a pipe are atomic as long as
the write size is <= PIPE_BUF, which is at least 512 bytes (on Linux it's a
full 4k). So pipes can be used as a naïve message queue: ensure each message
is under the limit in size, and they will not be split.

Anyone know if there's a similar guarantee for files?

~~~
brobinson
I read a while back that this PIPE_BUF restriction also applies to multiple
writers appending a file.

------
rwmj
I always thought it would be a good idea for system calls to support
transactions. Probably in a limited way because implementing general
transactions would require massive changes to the kernel. But it would be nice
to be able to do [error checking omitted]:

    
    
        begin ();
        fp = fopen ("file", "w");
        fputs (content, fp);
        fclose (fp);
        commit ();
    

It could solve the whole thing with ending up with zero-length files because
you didn't use the right incantation to update a file atomically on ext4
([https://thunk.org/tytso/blog/2009/03/12/delayed-
allocation-a...](https://thunk.org/tytso/blog/2009/03/12/delayed-allocation-
and-the-zero-length-file-problem/)).

In Unix v7 mkdir was not a system call. It was a setuid program implemented
using mknod + link. That was racy so the mkdir(2) system call was added. But
it could have been solved more generally (and more elegantly) by adding
transactions.

~~~
tobias3
Windows has it, but not many people seem to use it:
[https://msdn.microsoft.com/en-
us/library/windows/desktop/aa3...](https://msdn.microsoft.com/en-
us/library/windows/desktop/aa363764%28v=vs.85%29.aspx)

~~~
krylon
Beware, it appears to be deprecated:

"Microsoft strongly recommends developers utilize alternative means to achieve
your application’s needs. Many scenarios that TxF was developed for can be
achieved through simpler and more readily available techniques. Furthermore,
TxF may not be available in future versions of Microsoft Windows."

------
kevin_nisbet
Ah the good ol day's, this reminded me of some file transfer problems we used
to get.

We had multiple systems that generated usage records, and stored them to flat
file (think stuff that would end up on a bill). Because FTP the file was a
thing, some other system would come in any copy the file, but every once in
awhile there would be a partial file copied that would be missing records.
Yep, it was the good ol process was still writing to the file when the
collector decided to pick it up.

The first system I had control over, I made sure the vendor wrote to a
temporary directory, and then hard linked to the transfer directory when the
file operation was complete, knowing it avoided the race condition. I'm pretty
sure I had one of the few platforms that handled this correctly, from what I
remember we had corrupt files from almost all the platforms we bought.

Anyways, just because the system cost a million dollars doesn't mean it's any
good.

------
zAy0LfpBZLC8mAC
It should be noted that those filesystem operations are only atomic with
respect to an observer running on the same operating system incarnation.
Whether they are also atomic across power loss depends on the filesystem
(though with a modern journaling filesystem, that generally should be the
case).

~~~
gpderetta
That's what people would expect. But there was some drama around ext4, renames
and fsync a few years ago.

~~~
zAy0LfpBZLC8mAC
You mean around truncation and rewrite?

~~~
gpderetta
It is not necessarily about truncation. IIRC the problem was that rename
doesn't (didn't?) act as a full barrier on ext4 and the metadata write that
updates the name from the old file to the new file can be committed to disk
before the updates to the new file. This means that after a crash the new name
might point to a corrupted file.

The barrier behavior wasn't explicitly mandated by POSIX, but it is an
intuitive release consistency-like model which was implicitly expected by most
programmers.

edit: spurious words and parens.

------
throwaway76543
msync() with MS_INVALIDATE doesn't belong on this list. It has nothing to do
with atomic memory access. msync() is used when flushing a mapped file to
durable storage. I very often see this mistake of conflating flushing caches
with atomic access of memory. What's committed to durable storage has nothing
to do with what multiple processes will see when mapping a file.

All that's needed is the initial mmap to share a memory segment, then to use
atomic operations like CMPXCHG -- the x86 building block the later mentioned
gcc atomic macros leverage.

~~~
prodigal_erik
I haven't tested but I would expect MS_INVALIDATE on a large buffer to be much
faster than filling it a word at a time with __sync_val_compare_and_swap (each
causing its own bus transaction).

~~~
gpderetta
MS_INVALIDATE is likely a no-op on any modernish Unix, including Linux. It is
there to accommodate old systems with non-coherent mapped files and page
caches or even multiple mappings of the same file.

------
dmm
rename(2) was not atomic on OS X for years. It was finally fixed in Lion:
[http://www.weirdnet.nl/apple/rename.html](http://www.weirdnet.nl/apple/rename.html)

~~~
LukeShu
It should also be said that `mv`(1) is only atomic if the source and
destination are on the same filesystem.

~~~
mancerayder
So that implies that realistically there's a scenario where moving files or
directories from one filesystem to another and some interruption occurs can
lead to lost data?

~~~
Sanddancer
Very implausible. Mv across filesystems is usually implemented as a copy then
delete. Worst case, you'll have the data exist in both locations.

~~~
nshepperd
It's possible, in case of physical disconnection or power interruption. There
is no guarantee that the copied data will be flushed out of buffers into
nonvolatile storage before the delete is (unless the program asks for such a
flush and the system honors it).

------
netdog
The GCC Atomic Builtins mentioned in the article are not specific to Unix.
They are compiler constructs, and depend on specific architecture hardware
support. All x86 CPUs have such support for some years now. So these atomic
operations can also be used in non-Unix software running on x86 CPUs.

The GCC documentation lists other non-intel architectures which also have the
features required to support the atomic built-ins.

~~~
comex
Also, if you can depend on recent compilers you should probably be using the
standard C <stdatomic.h> or C++ <atomic> instead.

------
DonaldFisk
All system calls in ITS behaved as if they were atomic, using a mechanism
called PCLSRing:
[http://fare.tunes.org/tmp/emergent/pclsr.htm](http://fare.tunes.org/tmp/emergent/pclsr.htm)

Other systems which took a similar approach were WAITS, the Fluke Kernel, and
EROS and its successors.

------
estrabd
Always a nice reminder.

