How to make code that reads and writes to a variable thread safe?

The following (pseudo)code is not thread-safe:

mutable struct MutableState
     x::Int
end

function f(state)
     y = state.x 
     z = y^2
     state.x = z
end

state = MutableState(3)
# now run f(state) in parallel from many different threads 

My question is: how can I make this thread-safe? The key is that I read from state.x, do something, and then write to it. Can this be done using @atomic on the field x, and if so with which memory ordering requirement and on which operations do I put @atomic? (Just the write, or also the read?)

You can make operations like this thread safe by using a lock (a SpinLock is usually a good choice Multi-Threading ยท The Julia Language). However, this will basically nullify any parallelism so you should make sure that this is not the only part of the code that is run in parallel.

using Threads
Base.@kwdef mutable struct MutableState
     x::Int
     lock::SpinLock = SpinLock()
end

function f(state::MutableState)
     lock (state.lock) do 
         y = state.x 
         z = y^2
         state.x = z
    end
end

This will mean the read, compute and write are all done sequentially.