Skip to content

Commit 7d0037b

Browse files
peffgitster
authored andcommitted
thread-utils: introduce optional barrier type
One thread primitive we don't yet support is a barrier: it waits for all threads to reach a synchronization point before letting any of them continue. This would be useful for avoiding the LSan race we see in index-pack (and other places) by having all threads complete their initialization before any of them start to do real work. POSIX introduced a pthread_barrier_t in 2004, which does what we want. But if we want to rely on it: 1. Our Windows pthread emulation would need a new set of wrapper functions. There's a Synchronization Barrier primitive there, which was introduced in Windows 8 (which is old enough for us to depend on). 2. macOS (and possibly other systems) has pthreads but not pthread_barrier_t. So there we'd have to implement our own barrier based on the mutex and cond primitives. Those are do-able, but since we only care about avoiding races in our LSan builds, there's an easier way: make it a noop on systems without a native pthread barrier. This patch introduces a "maybe_thread_barrier" API. The clunky name (rather than just using pthread_barrier directly) should hopefully clue people in that on some systems it will do nothing. It's wired to a Makefile knob which has to be triggered manually, and we enable it for the linux-leaks CI jobs (since we know we'll have it there). There are some other possible options: - we could turn it on all the time for Linux systems based on uname. But we really only care about it for LSan builds, and there is no need to add extra code to regular builds. - we could turn it on only for LSan builds. But that would break builds on non-Linux platforms (like macOS) that otherwise should support sanitizers. - we could trigger only on the combination of Linux and LSan together. This isn't too hard to do, but the uname check isn't completely accurate. It is really about what your libc supports, and non-glibc systems might not have it (though at least musl seems to). So we'd risk breaking builds on those systems, which would need to add a new knob. Though the upside would be that running local "make SANITIZE=leak test" would be protected automatically. And of course none of this protects LSan runs from races on systems without pthread barriers. It's probably OK in practice to protect only our CI jobs, though. The race is rare-ish and most leak-checking happens through CI. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ca9d60f commit 7d0037b

File tree

3 files changed

+25
-0
lines changed

3 files changed

+25
-0
lines changed

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ include shared.mak
141141
#
142142
# Define NO_PTHREADS if you do not have or do not want to use Pthreads.
143143
#
144+
# Define THREAD_BARRIER_PTHREAD if your system has pthread_barrier_t. Barrier
145+
# support is optional and is only helpful when building with SANITIZE=leak, as
146+
# it is used to eliminate some races in the leak-checker.
147+
#
144148
# Define NO_PREAD if you have a problem with pread() system call (e.g.
145149
# cygwin1.dll before v1.5.22).
146150
#
@@ -2079,6 +2083,9 @@ ifdef NO_PTHREADS
20792083
else
20802084
BASIC_CFLAGS += $(PTHREAD_CFLAGS)
20812085
EXTLIBS += $(PTHREAD_LIBS)
2086+
ifdef THREAD_BARRIER_PTHREAD
2087+
BASIC_CFLAGS += -DTHREAD_BARRIER_PTHREAD
2088+
endif
20822089
endif
20832090

20842091
ifdef HAVE_PATHS_H

ci/lib.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ linux-musl)
385385
;;
386386
linux-leaks|linux-reftable-leaks)
387387
export SANITIZE=leak
388+
export THREAD_BARRIER_PTHREAD=1
388389
;;
389390
linux-asan-ubsan)
390391
export SANITIZE=address,undefined

thread-utils.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,22 @@ int dummy_pthread_init(void *);
5353
int online_cpus(void);
5454
int init_recursive_mutex(pthread_mutex_t*);
5555

56+
#ifdef THREAD_BARRIER_PTHREAD
57+
#define maybe_thread_barrier_t pthread_barrier_t
58+
#define maybe_thread_barrier_init pthread_barrier_init
59+
#define maybe_thread_barrier_wait pthread_barrier_wait
60+
#define maybe_thread_barrier_destroy pthread_barrier_destroy
61+
#else
62+
#define maybe_thread_barrier_t int
63+
static inline int maybe_thread_barrier_init(maybe_thread_barrier_t *b UNUSED,
64+
void *attr UNUSED,
65+
unsigned nr UNUSED)
66+
{
67+
errno = ENOSYS;
68+
return -1;
69+
}
70+
#define maybe_thread_barrier_wait(barrier)
71+
#define maybe_thread_barrier_destroy(barrier)
72+
#endif
5673

5774
#endif /* THREAD_COMPAT_H */

0 commit comments

Comments
 (0)