The interceptor for pthread_mutex_timedlock() was treating the EOWNERDEAD return value properly, and it instead assumed that that value is an actual error. TSAN therefore thought that the mutex was "not taken", and in the trace reports this appeared as if that robust mutex was not held at all for the recovery-after-a-death branch.
The test case is adopted from mutex_robust.cpp. In the thr() function, though, it is necessary to lock, write, unlock, and then lock again. If there was no unlock, then TSAN would report a race (which is was mutex_robust2.cpp tests for, so that's no change).
Discovered when trying to debug an issue in sysrepo; thank for Michal Vaško for a walkthrough through the original user code.
Bug: https://github.com/sysrepo/sysrepo/issues/2560
A git commit is available at https://github.com/jktjkt/llvm-project/commit/70e198ac563a2dfc20dabf082bcfcc28f4c5a52e .
Please make it long. Unit tests may have issues with variables less than 8 bytes due to tsan internal details.