This is an archive of the discontinued LLVM Phabricator instance.

[tsan] Add dispatch_group API interceptors and synchronization
ClosedPublic

Authored by kubamracek on Nov 27 2015, 9:31 AM.

Diff Detail

Repository
rL LLVM

Event Timeline

kubamracek updated this revision to Diff 41315.Nov 27 2015, 9:31 AM
kubamracek retitled this revision from to [tsan] Add dispatch_group API interceptors and synchronization.
kubamracek updated this object.
kubamracek added reviewers: dvyukov, kcc, glider, samsonov.
dvyukov added inline comments.Nov 30 2015, 6:43 AM
lib/tsan/rtl/tsan_libdispatch_mac.cc
72 ↗(On Diff #41315)

Is it compiled without warnings?
You use new_context within its own initialization statement. It is not yet initialized when you read it.

test/tsan/Darwin/gcd-groups-norace.mm
21 ↗(On Diff #41315)

assign to global after this statement

kubamracek updated this revision to Diff 41403.Nov 30 2015, 7:11 AM

Updating patch.

dvyukov accepted this revision.Dec 7 2015, 5:37 AM
dvyukov edited edge metadata.

LGTM

It seems there is some potential for group API misuse. For example, it is possible to "enter" inside of the async piece of work, instead of before spawning that async piece of work (quite common bug in Go WaitGroup's). Also, since there is ref-counting involved, it can be subject to race use-after-frees (e.g. dispatch_group_notify instead of dispatch_group_async).
Don't know how common are such bugs in real life. But on the other hand we won't know that until we implement detection of these bugs. You can look at https://github.com/golang/go/blob/master/src/sync/waitgroup.go for some ideas (search for "race").

lib/tsan/rtl/tsan_libdispatch_mac.cc
193 ↗(On Diff #41403)

Why don't we need Block_copy here, but need it below? Is the block automatically referenced from captured by another block?

This revision is now accepted and ready to land.Dec 7 2015, 5:37 AM
kubamracek added inline comments.Dec 7 2015, 5:50 AM
lib/tsan/rtl/tsan_libdispatch_mac.cc
193 ↗(On Diff #41403)

Technically, we don't need Block_copy for correctness, because the function that will store a reference to a block for later (dispatch_async in this case) will do it anyway. The reason for a Block_copy here is to get rid of a very common false positive, because Block_copy will call malloc and the malloc'd region is then accessed from another thread. Since this happens *inside* dispatch_async, it's already after we've Release()'d. So I'm just forcing the allocation to happen early (before Release()).

I'm not calling Block_copy here, because WRAP(dispatch_async) will call it anyway. Below, we're calling REAL(...), so we need to Block_copy.

This revision was automatically updated to reflect the committed changes.