
> (require racket/flonum racket/futures)
> (define N 1000)
> (define v (make-flvector 1 0.))
> (for/async ([i (in-range N)]) (flvector-set! v 0 (+ 1 (flvector-ref v 0))))
> v
(flvector 994.0)
Atomicity matters.

Note that the delay incurred by non-fl operations makes the matter worse. With fl operations it’s closer to 999, but the problem of course still appears: > (define N 10000)
> (define v (make-flvector 1 0.))
> (for/async ([i (in-range N)]) (flvector-set! v 0 (fl+ 1. (flvector-ref v 0))))
> v
(flvector 9980.0)

Heh. In C, this was an interview question I was once asked (pseudo-code as I don’t remember it exactly):
int x = 0;
void increment() {
x++;
}
void test() {
pthread_t t1, t2;
pthread_create(&t1, NULL, increment, NULL);
pthread_create(&t2, NULL, increment, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
// what is the value of `x` at this point?
}
Then lots of “fun” follow-ups:
• What if we use ++x
instead? • What if we declare x
as volatile
? • etc.

the answer is “undefined behavior” in all cases, right?

Right. It’ll basically be “1 or 2” depending on when the context switches happen.
However, adding the volatile
keyword makes things considerably trickier since x
has the potential to never be in a register given the CPU architecture, when context switches are allowed to happen, etc.
So, for some CPUs, adding volatile
can work as a poor-man’s mutex, but certainly not in all cases.

I think it’s worse than that — the compiler/runtime is licensed to give you any behavior at all.

pretty much

I think the “modern” equivalent of this issue I see lots of programmers fall prey to is this (using JS, but holds in many languages): for (let i =0;i < 10; i++) {
new Promise((resolve, reject) => resolve(i * 10));
}
Basically, does the promise’s closure capture the variable i
or the value of i
?

A fortune cookie I got last night I thought others here may enjoy:

I got this one instead: > The cod is mightier than the swordfish

lol