When concurrently appending to the same slice, it's easy to get it to memcpy past the end due to struct (slice in this case) copy being non-atomic, i.e. if cap is copied before arr, concurrent code can use old arr with a new cap.
My issue isn't with the non-atomicity of the copy, or with slices being unsafe for data races, and I'm not making a case for communicating by sharing memory ;) Rather, it's about the failure mode being not only data loss in the particular slice (to be expected in a data structure that's not thread-safe) but also spooky actions at a distance (random heap writes, "impossible" crashes).
In practical programs, concurrent appends may be inadvertent. Packages use slices casually, in ways the author might not think to advertise, and the user might not think to inspect. Suppose a debug logging mechanism using a global slice - a slice which is not even read by anyone except when debugging - and then suppose someone starts concurrently calling a function which uses the logging function. This could result in a heap corruption that would be difficult to track down.
There might be practical solutions, e.g.
- make it so that
arr is copied before cap
- store the capacity in the head of the
arr buffer rather than in cap
(Apologies if an obvious dup, I couldn't find one.)
When concurrently
appending to the same slice, it's easy to get it to memcpy past the end due to struct (slice in this case) copy being non-atomic, i.e. ifcapis copied beforearr, concurrent code can use oldarrwith a newcap.My issue isn't with the non-atomicity of the copy, or with slices being unsafe for data races, and I'm not making a case for communicating by sharing memory ;) Rather, it's about the failure mode being not only data loss in the particular slice (to be expected in a data structure that's not thread-safe) but also spooky actions at a distance (random heap writes, "impossible" crashes).
In practical programs, concurrent appends may be inadvertent. Packages use slices casually, in ways the author might not think to advertise, and the user might not think to inspect. Suppose a debug logging mechanism using a global slice - a slice which is not even read by anyone except when debugging - and then suppose someone starts concurrently calling a function which uses the logging function. This could result in a heap corruption that would be difficult to track down.
There might be practical solutions, e.g.
arris copied beforecaparrbuffer rather than incap(Apologies if an obvious dup, I couldn't find one.)