-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Fix data race on global pools arrays of pool_freelist
#12755
Conversation
Co-authored-by: Olivier Nicole <olivier@chnik.fr>
To address some possible confusion: manipulations of this global free list is generally protected by a mutex, but there is exactly one exception, Lines 301 to 309 in 8ec2b3d
This read consequently can race with all writes into the same arrays, which is technically UB. |
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.
I am not convinced by your choice of relaxed
. Except for the one short-cut check that was outside the lock, these accesses only occur (in debug mode or) in code that runs on the first pool allocation that follows the termination of another domain, that is, it is very rare. I think that the performance gains of relaxed
compared to a pleasant atomic access are neglectible.
runtime/shared_heap.c
Outdated
if( !pool_freelist.global_avail_pools[sz] && | ||
!pool_freelist.global_full_pools[sz] ) | ||
if( !atomic_load_acquire(&pool_freelist.global_avail_pools[sz]) && | ||
!atomic_load_acquire(&pool_freelist.global_full_pools[sz]) ) |
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.
I would expect a relaxed
here if the intention is to have a fast, approximate check that corresponds to the previous version. acquire
is probably fine though.
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.
My intention was to avoid taking the lock if not necessary, but now that I have to argue about it, I'm no longer sure it was achieving it. Relaxed operation will be fine.
In principle, I’m not at all opposed to use SC atomics instead of relaxed if the performance cost is negligible: it makes the code much more readable. I see one other place where such SC atomic operations on global pools will occur, however: through |
|
These accesses are protected by the |
I would have preferred the less verbose code with normal C reads/writes, but if you care about using relaxed operations explicitly, those who do the work decide. |
There is a failure on the CI:
|
It's nice to have Clang to spot these issues. Sorry I didn't notice it before! |
Is this only waiting on a Changes entry? |
I guess it is. |
|
This PR, a joint work with @OlivierNicole, addresses data races on
global_avail_pools
andglobal_full_pools
members of thestruct pool_freelist
inshared_heap.c
.Domains can access the
global_(avail|full)_pools
arrays in parallel while trying to adopt a pool withpool_global_adopt
, so these arrays must be made of atomic pointers.Since the
pool_freelist
struct is not exposed outside of the runtime, the choice was made to use a proper_Atomic
qualifier rather than avolatile
one. The downside is that read and write accesses have to be done through theatomic_(load|store)_relaxed
functions to not cause more synchronisation than intended, which makes the code a bit more verbose.