Question

I am just going through Clojure Koans and I am now playing with atoms. My question is not related to Koans, but a general one.

Consider the following (shortened) example from Koans:

(def atomic-clock (atom 0))

(meditations
    (= 1 (do
        (swap! atomic-clock inc)
            @atomic-clock)))

The documentation for swap! states that the function it is applied to (inc in this case) could be called multiple times and thus the function should be free from side effect.

Obviously, inc is free from side effects, but is not idempotent. Does this mean, that the assertion above may actually still fail? I.e. that in the case the function is indeed called multiple times, the value of the atom is incremented more than once?

Was it helpful?

Solution

The function passed to swap! may be called multiple times if there are multiple threads contending to modify the Atom. As long as it is free of side-effects, however, only the return of the final call will be reflected in the Atom's causal history.1

Here's one possible scenario:

  1. Thread 1 attempts (swap! atomic-clock inc).

  2. Thread 2 attempts the same.

  3. Thread 2 manages to perform its swap! first.

  4. Thread 1 attempts a compare-and-swap on the atom and fails, because its notion of the original value is now out of date.

  5. Thread 1 retries and successfully commits.

Here there are three calls to inc, two on thread 1, one on thread 2.

The fact that inc is not idempotent is not a problem.


1 Considered abstractly; Clojure does not actually store history information for Atoms.

OTHER TIPS

No, because the function will be called multiple times with the original value. If the swap succeeds, the function won't be called again with the new value that the function produced. The "may be called again" situation is where the atom's value had already been altered by another part of your program, so the swap is retried. As long as you return the same result for the same arguments, this is fine — but if, say, you were reading data from a stream and sticking it in the atom, that would be problematic.

No, it really means that if you have more than one thread swapping on the atom, the function may retry in order to use the correct value.

Picture the following executions:

Thread1:

  • Read atomic-clock 0
  • inc atomic-clock
  • update atomic-clock with 1

Thread2:

  • Read atomic-clock still 0
  • inc atomic-clock
  • update atomic-clock with 1 ==> WRONG should be 2

In order to call your function with the atom value, current value must be read.

But if current value is modified in another thread, the whole read/update process must retry, so your function gets called again.

Meaning that if your function launchs a rocket, it will launch two rockets, even if the first time it's called with 0 and returns 1, and the second time it's called with 1 and returns 2.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top