-
Notifications
You must be signed in to change notification settings - Fork 213
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
steamcompmgr, wlserver: fix various data races found w/ thread sanitizer #1405
base: master
Are you sure you want to change the base?
steamcompmgr, wlserver: fix various data races found w/ thread sanitizer #1405
Conversation
c0251ce
to
a542451
Compare
Please don't do this Just make them atomics. |
You also don't really need to do explicit acquire/release here. We can optimize those later. Just keeo them default (seq_cst) |
ok
If you're sure, then I'll change everything to seq_cst but on the otherhand:
on the steam deck's zen 2-based-cpu |
a542451
to
d3625ed
Compare
@Joshua-Ashton |
You don't need to specify it... you can just use = You can also simplify a lot of your code with std::exchange |
ok
Uhhhhh I'm not sure if you meant to say if you actually meant to say I had tried to replace some atomic load+atomic stores with one compare and exchange to get around that issue, but wasn't able to use it without getting into the same issue as w/ atomic exchange. EDIT: I just realized that However, c++ std::atomic exchange's function prototype is: |
ddc0f15
to
69a4a18
Compare
@Joshua-Ashton |
src/wlserver.hpp
Outdated
bool bCursorHidden = true; | ||
bool bCursorHasImage = true; | ||
std::atomic<uint64_t> ulLastMovedCursorTime = 0; | ||
std::atomic<char> bCursorHidden = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why char?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why char?
For some reason, you can't do fetch_or
on a boolean atomic...
Which in the one case where I use it, I can't cleanly replace it with something like an atomic exchange.
only integral atomics can do stuff like fetch_or
https://en.cppreference.com/w/cpp/atomic/atomic#Specializations_for_integral_types
(note that technically on x86_64, w/ how I grab the old value of the atomic when using fetch_or
, fetch_or
actually gets compiled into an atomic compare exchange (which is still lock-free) because x86_64 lock or
instruction does not return the old value of the atomic. Funnily enough, aarch64 and riscv [w/ the right atomic isa support] have their own version of lock or
that actually returns the old value of the atomic.)
So I use char, since a bool, when stored in a struct/class (in most implementations), actually occupies the same amount of memory as a char.
Would you prefer I use uint8_t
instead of char?
I believe most modern cpus should support lock-free atomics on 8 bit values, so I think it should be fine?
I know that basically all x86_64 cpus support it,
plus the same aarch64 isa extension that provided support for stuff like atomic increment also gave support for operations on a wider variety of atomic sizes, including 8bit:
https://devblogs.microsoft.com/oldnewthing/20220811-00/?p=106963
54f8202
to
6ed69b0
Compare
@Joshua-Ashton |
6ed69b0
to
387113d
Compare
For testing w/ TSAN, I added on the meson flags
-Db_sanitize=thread --buildtype=debugoptimized
when compiling gamescopeI've only done tests w/ running gamescope in nested mode on my X11 host desktop, so this PR might need testing on a wayland host w/ the gamescope wayland backend.
I used the following command to test the latest commit from master w/ TSAN:
TSAN_OPTIONS="suppressions=${HOME}/gamescope/suppress_mangoapp" build/src/gamescope -- vkcube |& tee gamescope_tsan.log
note that I had to suppress
mangoapp_update()
(thesuppress_mangoapp
files is only one line:race:mangoapp_update
) to avoid the log getting filled with messages about itI observed at least three unique data race messages, occurring after gamescope + vkcube were actively running (so after initialization), that would reliably get triggered whenever I did the following in order:
SUMMARY: ThreadSanitizer: data race ../src/wlserver.cpp:337 in bump_input_counter
SUMMARY: ThreadSanitizer: data race ../src/wlserver.cpp:2398 in wlserver_mousebutton(int, bool, unsigned int)
SUMMARY: ThreadSanitizer: data race ../src/wlserver.cpp:2379 in wlserver_mousewarp(double, double, unsigned int, bool)
Full log: https://gist.github.com/sharkautarch/4a8cf292d8b70d133904708ac29465ed
While there were a lot of TSAN warnings which could be false positives and/or non-issues,
the data races addressed by this PR seemed like a bigger deal
EDIT:I just realized thatif (bPrevState & suspended)
should beif (!bPrevState & suspended)
OOPSIESWill fix that soon^FIXED
Also, some explanations for some of the weird things in this PR:
bCursorHidden
bestd::atomic<char>
in order to be able to use atomicfetch_or
. This should be safe because I made sure thatbCursorHidden
is always initialized and assignedbool
values which means that the variable will always be either 1 or 0I useatomic_ref
w/inputCounter
instead of a normalatomic
becauseinputCounter
is passed into an Xlib function. That means that the Xlib function is doing a non-atomic write toinputCounter
, but TSAN doesn’t seem to complain, so either it’s a false negative or the threads just never actually race at that spot… I’ve been using gcc’s thread sanitizer, so maybe this might warrant checking w/ clang’s thread sanitizer…!EDIT: for the second bullet point, now that I think about it, Xlib is a shared library, which means that the only way for TSAN to catch races from Xlib function calls would be to rebuild Xlib w/ TSAN. So I think I’ll need to edit this PR again^FIXED^Just realized that XChangeProperty doesn't modify its input data (or at least I don't think it does). I updated the PR again
last update:to deal with the one remaining edge case I saw, I changed all instances of
wlserver.bCursorHidden.store(!wlserver.bCursorHasImage, std::memory_order_release);
to:
std::atomic<char> atomicTrue{true};
atomicTrue.compare_exchange_strong(wlserver.bCursorHidden, wlserver.bCursorHasImage);
which makes the line a single CAS RMW operationNot sure if the change is actually necessary,and the compare-and-exchange is definitely slower than a load+store.So let me know whether you think I should undo that...(edit: for now I've decided to changecompare_exchange_strong
tocompare_exchange_weak
, tho this only makes a difference w/ non-x86 architectures)^ I undid the CAS thing because I realized it didn't work the way I was thinking it would