The thread-safety guarantees can only be obeyed if the arguments are thread-safe as well.
For example, take a java.util.concurrent.ConcurrentHashMap, which is thread-safe. If your value object is mutated concurrently, and is not thread-safe, you can't expect anything sensible to happen if you call containsValue().
Likewise, String's char[] constructor can only be thread safe if there are no concurrent changes to the argument.
> The issue is that the ctor checks for a property (all chars are under 256) then assumes that holds indefinitely.
You could say that this is a classical TOCTOU bug as the ctor assumes that a mutable argument does not mutate.
The core question[s] here are whether this assumption is and should be in the contract.
Protection against these kinds of bugs is only possible with deep copies of mutable arguments or COW semantics. So you either implement callee defensively and slow down general case (remember, this is stdlib), or you leave concurrency control to the caller. Probably every sane general purpose language does the latter.
> You could say that this is a classical TOCTOU bug as the ctor assumes that a mutable argument does not mutate.
Indeed.
> Protection against these kinds of bugs is only possible with deep copies of mutable arguments or COW semantics.
In the general case yes, but in this case no, you can solve the issue and improve performances by implementing the thing correctly, in a single pass over the input. This way you don’t get a split-brain situation.
For example, take a java.util.concurrent.ConcurrentHashMap, which is thread-safe. If your value object is mutated concurrently, and is not thread-safe, you can't expect anything sensible to happen if you call containsValue().
Likewise, String's char[] constructor can only be thread safe if there are no concurrent changes to the argument.