Race condition behaviors

On a formal level, such a data race has undefined behavior, and Swift is not constrained to do anything sensible.

On an implementation level, Swift will usually keep different values in non-overlapping, well-aligned memory, and it will usually perform loads and stores of small, trivial types using single load and store instructions, which are usually architecturally guaranteed to not introduce spurious tearing on well-aligned memory. However, the implementation isn't required to do any of these things, because data races have undefined behavior. And this is not merely theoretical for most types:

  • Loads and stores of non-trivial types can corrupt the heap under a data race. For example, two concurrent stores of a class reference can race to release the same object instead of being arbitrarily ordered.
  • Loads and stores of trivial types will not directly crash under data races, but they may exhibit tearing or other "unnatural" behavior. For example, there are situations in which Swift can end up copying an Int with memcpy, such as if it's part of a larger aggregate. There is no guarantee that Swift will use "natural" loads and stores from the underlying architecture.
  • Load and stores from different stored properties of a class will not interfere with each other, at least for all current classes. However, this guarantee may be weakened in the future, and it does not extend to structs. For example, Swift might choose to "pack" two different Bool struct properties into the same byte of storage, and it does not have to use atomic sequences to update them. (Swift does not currently perform this optimization, but it's permitted because of the rules about data races on structs. It would not be permitted for class properties.)
17 Likes