-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Potential memory order inconsistency on freelist block reusage #404
Comments
fix issue cameron314#404
@cameron314 This issue looks quite serious. To address it, I've submitted #406, which is based on this commit. When you have a moment, could you please review the changes? Thank you for your time and consideration. |
I can confirm this TSan report indicates it thinks the block was not handed off cleanly in the It is not clear to me whether or not this is a false positive. More analysis is needed. At the root of the free list implementation are CAS operations on If there is a bug, blindly changing memory ordering until TSan stops complaining does not necessarily indicate the bug is fixed. Actual human logic needs to be involved. |
Think I found one case where the |
Sorry, but I do not understand how line 1607 is irrelevant if it is executed (on my machine, with the example above). And, yes, if thead B releases the block (after releasing the last element) and thread C acquires that block, there is definitely a clear happens-before relation between them. However, when thread A releases a non-last element from that block, it does not interact with In fact, I do not believe in false positives with modern TSAN, unless they are extremly rare or thread fences are in use (ofc when talking about C++ memory model and not about actual races on a particular architecture). |
Forgot to mention that, as expected, e74b07d does not fix the problem. |
Ah, I misread your initial analysis. I see what you mean now. It's not about the thread freeing the block but about the other dequeue operations on that block before that. Thank you, this is helpful. It seems I also reread the code too quickly. Line 1607 is skipped with the default block size for explicit producers, but not implicit ones. Yes, the commit I pushed is for an unrelated potential issue. |
… that block's reuse when empty, affecting implicit producers (see #404)
I don't have TSan set up at the moment, please let me know if the latest commit fixes this particular warning. Based on #406, I'm guessing there are others that are unrelated to this one. |
I can't test this commit at the moment, but as I mentioned earlier, one change on line 1607 completely resolves the warnings in the example. I suppose there might be a similar issue with flags in the if-branch as well. Maybe it will be possible to introduce TSAN-tests in the future, starting with the example above? |
Also, in terms of performance, it may be better to use release memory order with acquire-fence on the last increment, as described here https://www.boost.org/doc/libs/1_53_0/doc/html/atomic/usage_examples.html#boost_atomic.usage_examples.example_reference_counters.discussion |
Hi! So I noticed that TSAN complains about races inside concurrentqueue. I was able to reproduce it with the following minimal example:
I believe this race (in terms of C++ memory model) happens like this:
T0: Thread A reads from memory slot S.
T1: Thread A releases memory slot S of block X.
T2: Thread B releases last slot of block X, pushes block into the freelist.
T3: Thread C acquires block X from the freelist, writes to slot S.
I guess
release
in this and similar places should beacq_rel
(orrelease
+acquire fence
on the last increment):https://github.com/cameron314/concurrentqueue/blob/master/concurrentqueue.h#L1607
to build happens-before relation between the slot release (thread A) and the whole block release (thread B) and subsequent push to the freelist.
(Tested it, TSAN stopped complaining).
I am not so good with C++ memory model, so maybe I am missing something here?
The text was updated successfully, but these errors were encountered: