New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SR-6604] Are Array writes race free in case of copying an array? #49154
Comments
It is part of the language model that this is safe, so clearly we have to get it right. See also SR-6543. |
Then we have to fix the implementation of array and insert a release fence (or a release memory operation on the reference count). |
Note that if queue.dispatch has the fence semantics then we are fine. But I am not assuming this because then we would not be relying on array to give us the race freedom. What I am asking is that assume that dispatch does not have fence semantics: is there a language level race or not? If there is no language level race then we have a problem. |
The programmer can rely on race freedom on the array buffer because it is semantically part of the array's value. An array has the same semantics as any struct w.r.t. races. queue.async would need to have a memory fence, otherwise you wouldn't be able to pass any (by-value) data into the closure. |
Arnold and I discussed this some in person, and I think we boiled it down to these three examples:
That is, this isn't about Array specifically; it's about closure captures. The situation does get worse with CoW types (i.e. the second example has two races), but that's not the fundamental scaryness here. However, the point about DispatchQueue.async providing a memory barrier anyway is a good one—most concurrency utilities that operate in terms of closures probably won't run into this in practice. (cc @devincoughlin as well, for previous discussions of both exclusivity and "value types are safe") |
cc phabouzit (JIRA User) for Dispatch insight |
Comment by Pierre Habouzit (JIRA) This: On any sane platform that I know of, spawning a thread has barriers, condition variables are supposed to be used with a mutex that have the right barriers etc.. Dispatch e.g. for dispatch_async:
I don't think Swift should protect against utterly broken synchronization primitives that are used for data synchronization but do not provide the most basic barriers. these are broken. |
:-) That's fair. In that case, we should be okay. We won't ever have a race on the initial value that gets captured by a closure because that closure somehow has to make it onto another thread (barrier), and then any other races are at least explainable to people. And catchable by TSan. If this becomes relevant again in some "system-level Swift" effort, well, they can just take extra care in whatever bizarre situation they have using closures across threads without barriers. |
Comment by Pierre Habouzit (JIRA) Even at the "system" level, people have to be mindful of data synchronization themselves, else you have to pessimize for concurrent use and your runtime would have to store-release about everything which is a broken model.
|
Additional Detail from JIRA
md5: a0c8b7713ccdc6ab175a31af44e161a6
relates to:
Issue Description:
I have heard people say that they can rely on copy-on-write behavior of Array to guarantee race-freedom when working with arrays in certain situations (the array is copied). I do not believe this to be true in today's Array implementation. Here is my reasoning.
I am going to use c++'s memory model and assume that:
strong_retain has acquire semantics on the reference
is_unique has acquire semantics on the reference
strong_release has release semantics on the reference
(Swift's reference counting implementation actually uses more the more relaxed consume semantics but for the discussion this modeling is good enough)
Here is my example that shows a race even though we have created a copy of the array.
Here is the code that will get executed on thread 1:
Here is the code that will get executed on dispatch thread:
Note, that there is no release-acquire handshake that establishes a happens-before relationship between thread1's write to the array and thread 2's is_unique check. So the following racing interleaving is allowed:
I don't believe this race to be a problem because there is a race at the language level on the captured copy. If I am wrong on this point we have a problem.
But I don't believe that we can say the programmer can rely on race freedom because arrays are copy-on-write. If we wanted this property we would have to insert a release memory fence after the array mutation.
To clarify I am not talking about the situation where the copy was created under synchronization and then we have two writes to distinct values. This case is of course okay.
The text was updated successfully, but these errors were encountered: