Deadlock with SharedMutex across multiple DSOs and visibility=hidden
- Dominant language
- C++
- Stars
- 30.5k
- Forks
- 5.9k
- PR merge metrics
- No merged PRs in 30d
Description
### Summary
When accessing a `folly::SharedMutex` instance from multiple DSOs and program and folly compiled with `-fvisibility=hidden`, a deadlock is seen when at least one thread is attempting to acquire a write lock. Backtraces suggest that the writer is waiting for some outstanding reader to unlock; but no such reader appears to have the lock acquired.
* Version: : v2021.04.12.00 and v2010.08.24.00 both affected.
* Environment: GCC 7.5.0, Ubuntu 18.04
* Reproducer at: https://github.com/daverigby/folly_SharedMutexIssue
Note: this is a similar issue to #1431.
### Details
From my limited understanding of `SharedMutex`, what seems to be occurring is that there's multiple copies of the `SharedMutex<>::deferredReaders` static array present in the process; and as such the tracking of which readers are outstanding is broken. I believe this is due to `deferredReaders` having local (hidden) visibility - for example examining the two DSOs in my test program, note the LOCAL visibility:
```
$ readelf -sW main | c++filt | grep 'folly::SharedMutexImpl::deferredReaders
$ readelf -sW libshared.so | c++filt | grep 'folly::SharedMutexImpl::deferredReaders
```
If I instantiate SharedMutex with it's own tag:
```
struct FOLLY_EXPORT MyTag {};
using Mutex = folly::SharedMutexImpl;
```
_and_ modify the declaration of `SharedMutexImpl` to mark it with `FOLLY_EXPORT` the test doesn't hang (run for > 1 hour with no issues):
```
diff --git a/folly/SharedMutex.h b/folly/SharedMutex.h
index 95faa76f5..44102168b 100644
--- a/folly/SharedMutex.h
+++ b/folly/SharedMutex.h
@@ -330,3 +330,3 @@ template <
bool TrackThreadId = false>
-class SharedMutexImpl : std::conditional_t<
+class FOLLY_EXPORT SharedMutexImpl : std::conditional_t<
TrackThreadId,
```
Symbol visibility after this change (MyTag version):
```
$ readelf -sW libshared.so | c++filt | grep 'folly::SharedMutexImpl::deferredReaders
```
Note both the change to `SharedMutexImpl`'s declaration _and_ the explicit tag is needed for the visibility to change (and test to pass) for me. I don't exactly understand why that is the case; given I only need to change the tag I assume that the default ``void`` tag has hidden visibility (possibly due to that being the default I set?) - but none of the other types used in the template instantiation need to be changed to fix the problem...
Contributor guide
Assessment
This issue has not been assessed yet.