Search code examples
cmultithreadingconcurrencyclojureimmutability

Can we prevent shared mutable state if we only allow local variables to be mutable?


In a non-OOP programming language, like C, If we only allow local variables to be mutated in every possible way (change internal fields, re-assigning, ...) but disallow mutation of function arguments, will it help us prevent shared mutable state?

Note that in this case, function main can start 10 threads (functions) and each of those 10 threads will receive an immutable reference to the same variable (defined in main). But the main function can still change the value of that shared variable. So can this cause problem in a concurrent/parallel software?

I hope the question is clear, but let me know if it's not.

P.S. Can "software transactional memory (STM)" solve the potential problems? Like what Clojure offers?


Solution

  • Yes and no... this depends on the platform, the CPU, the size of the shared variable and the compiler.

    On an NVIDIA forum, in relation to GPU operations, a similar question was very neatly answered:

    When multiple threads are writing or reading to/from a naturally aligned location in global memory, and the datatype being read or written is the same by all threads, and the datatype corresponds to one of the supported types for single-instruction thread access ...

    (Many GPU single-instruction can handle 16 Byte words (128bit) when it's known in advance, but most CPUs use single-instruction 32bits or 64bit limits)

    I'm leaving aside the chance that threads might read from the CPU registers instead of the actual memory (ignoring updates to the data), these are mostly solvable using the volatile keyword in C.

    However, conflicts and memory corruption can still happen.

    Some memory storage operations are handled internally (by the CPU) or by your compiler (the machine code) using a number of storage calls.

    In these cases, mostly on multi-core machines (but not only), there's the risk that the "reader" will receive information that was partially updated and has no meaning whatsoever (i.e., half of a pointer is valid and the other isn't).

    Variables larger than 32 bits or 64 bits, will usually get updated a CPU "word" (not an OS word) at a time (32bits or 64 bits).

    Byte sized variables are super safe, That's why they are are often used as flags... but they should probably be handled using the atomic_* store/write operations provided by the OS or the compiler.