Skip to content

Commit 7a799a9

Browse files
committed
[tsan] Workaround uninstrumented library code
We get rare reports from TSan that there is a race between modifying the UnitEventInfoDeque and its own destructor. This appears to be caused by uninstrumented code in the indexstore library as well as the underlying OS file watching library (FSEvents, inotify, etc.), which prevents TSan from realizing that the event stream callback and the event stream finalizer are never called concurrently. We workaround by introducing an unnecessary mutex that we lock around the requisite calls. rdar://69514830
1 parent 8ac9e5e commit 7a799a9

File tree

1 file changed

+47
-7
lines changed

1 file changed

+47
-7
lines changed

include/indexstore/IndexStoreCXX.h

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "llvm/ADT/Optional.h"
2020
#include "llvm/ADT/STLExtras.h"
2121
#include "llvm/ADT/SmallString.h"
22+
#include "llvm/Support/Mutex.h"
2223
#include <ctime>
2324

2425
namespace indexstore {
@@ -203,6 +204,29 @@ class IndexStore {
203204
typedef std::function<void(UnitEventNotification)> UnitEventHandler;
204205
typedef std::function<void(indexstore_unit_event_notification_t)> RawUnitEventHandler;
205206

207+
private:
208+
struct EventHandlerContext {
209+
RawUnitEventHandler handler;
210+
#if __has_feature(thread_sanitizer)
211+
std::unique_ptr<llvm::sys::Mutex> eventHandlerMutex;
212+
#endif
213+
214+
EventHandlerContext(RawUnitEventHandler handler) : handler(std::move(handler)) {
215+
#if __has_feature(thread_sanitizer)
216+
eventHandlerMutex = std::make_unique<llvm::sys::Mutex>();
217+
#endif
218+
}
219+
220+
~EventHandlerContext() {
221+
#if __has_feature(thread_sanitizer)
222+
// See comment in event_handler_finalizer.
223+
assert(!eventHandlerMutex);
224+
#endif
225+
}
226+
};
227+
228+
public:
229+
206230
void setUnitEventHandler(UnitEventHandler handler) {
207231
auto localLib = std::weak_ptr<IndexStoreLibrary>(library);
208232
if (!handler) {
@@ -212,7 +236,7 @@ class IndexStore {
212236

213237
#pragma clang diagnostic push
214238
#pragma clang diagnostic ignored "-Wc++14-extensions"
215-
auto fnPtr = new RawUnitEventHandler([handler, localLib=std::move(localLib)](
239+
auto fnPtr = new EventHandlerContext([handler, localLib=std::move(localLib)](
216240
indexstore_unit_event_notification_t evt_note) {
217241
if (auto lib = localLib.lock()) {
218242
handler(UnitEventNotification(evt_note, lib));
@@ -223,13 +247,29 @@ class IndexStore {
223247
}
224248

225249
private:
226-
static void event_handler(void *ctx, indexstore_unit_event_notification_t evt) {
227-
auto fnPtr = (RawUnitEventHandler*)ctx;
228-
(*fnPtr)(evt);
250+
static void event_handler(void *ctx_, indexstore_unit_event_notification_t evt) {
251+
auto ctx = (EventHandlerContext*)ctx_;
252+
253+
#if __has_feature(thread_sanitizer)
254+
// See comment in event_handler_finalizer.
255+
llvm::sys::ScopedLock L(*ctx->eventHandlerMutex);
256+
#endif
257+
258+
(ctx->handler)(evt);
229259
}
230-
static void event_handler_finalizer(void *ctx) {
231-
auto fnPtr = (RawUnitEventHandler*)ctx;
232-
delete fnPtr;
260+
static void event_handler_finalizer(void *ctx_) {
261+
auto ctx = (EventHandlerContext*)ctx_;
262+
263+
#if __has_feature(thread_sanitizer)
264+
// We need to convince TSan that the event handler callback never overlaps the
265+
// destructor of the context. We use `eventHandlerMutex` to ensure TSan can
266+
// see the synchronization, and we need to move the mutex out of the context
267+
// so that it can be held during `delete` below.
268+
auto mutexPtr = std::move(ctx->eventHandlerMutex);
269+
llvm::sys::ScopedLock L(*mutexPtr);
270+
#endif
271+
272+
delete ctx;
233273
}
234274

235275
public:

0 commit comments

Comments
 (0)