issue-496: Fix possible deadlock in thread+fork

This patch adds a pthread_atfork handler to set the internal
locks in consistent state.
This commit is contained in:
Adhemerval Zanella 2014-02-02 19:21:53 -02:00
parent 73ccf4d1b9
commit 05c33f5308
5 changed files with 58 additions and 0 deletions

11
configure vendored
View File

@ -15114,6 +15114,17 @@ _ACEOF
fi
done
# for turning off services when run as root
for ac_func in fork
do :
ac_fn_c_check_func "$LINENO" "fork" "ac_cv_func_fork"
if test "x$ac_cv_func_fork" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_FORK 1
_ACEOF
fi
done
# for the pthread_atfork setup
for ac_header in features.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "features.h" "ac_cv_header_features_h" "$ac_includes_default"

View File

@ -134,6 +134,7 @@ AC_CHECK_TYPES([struct mallinfo],,, [#include <malloc.h>])
AC_CHECK_TYPES([Elf32_Versym],,, [#include <elf.h>]) # for vdso_support.h
AC_CHECK_FUNCS(sbrk) # for tcmalloc to get memory
AC_CHECK_FUNCS(geteuid) # for turning off services when run as root
AC_CHECK_FUNCS(fork) # for the pthread_atfork setup
AC_CHECK_HEADERS(features.h) # for vdso_support.h
AC_CHECK_HEADERS(malloc.h) # some systems define stuff there, others not
AC_CHECK_HEADERS(sys/malloc.h) # where some versions of OS X put malloc.h

View File

@ -79,6 +79,16 @@ class CentralFreeList {
// page full of 5-byte objects would have 2 bytes memory overhead).
size_t OverheadBytes();
// Lock/Unlock the internal SpinLock. Used on the pthread_atfork call
// to set the lock in a consistent state before the fork.
void Lock() {
lock_.Lock();
}
void Unlock() {
lock_.Unlock();
}
private:
// TransferCache is used to cache transfers of
// sizemap.num_objects_to_move(size_class) back and forth between

View File

@ -56,6 +56,9 @@
/* Define to 1 if you have the <features.h> header file. */
#undef HAVE_FEATURES_H
/* Define to 1 if you have the `fork' function. */
#undef HAVE_FORK
/* Define to 1 if you have the `geteuid' function. */
#undef HAVE_GETEUID

View File

@ -39,6 +39,36 @@
namespace tcmalloc {
#if defined(HAVE_FORK) && defined(HAVE_PTHREAD)
// These following two functions are registered via pthread_atfork to make
// sure the central_cache locks remain in a consisten state in the forked
// version of the thread.
static void CentralCacheLockAll()
{
Static::pageheap_lock()->Lock();
for (int i = 0; i < kNumClasses; ++i)
Static::central_cache()[i].Lock();
}
static void CentralCacheUnlockAll()
{
for (int i = 0; i < kNumClasses; ++i)
Static::central_cache()[i].Unlock();
Static::pageheap_lock()->Unlock();
}
#endif
static inline void SetupAtForkLocksHandler()
{
#if defined(HAVE_FORK) && defined(HAVE_PTHREAD)
pthread_atfork(CentralCacheLockAll, // parent calls before fork
CentralCacheUnlockAll, // parent calls after fork
CentralCacheUnlockAll); // child calls after fork
#endif
}
SpinLock Static::pageheap_lock_(SpinLock::LINKER_INITIALIZED);
SizeMap Static::sizemap_;
CentralFreeListPadded Static::central_cache_[kNumClasses];
@ -49,6 +79,7 @@ PageHeapAllocator<StackTraceTable::Bucket> Static::bucket_allocator_;
StackTrace* Static::growth_stacks_ = NULL;
PageHeap* Static::pageheap_ = NULL;
void Static::InitStaticVars() {
sizemap_.Init();
span_allocator_.Init();
@ -61,6 +92,8 @@ void Static::InitStaticVars() {
for (int i = 0; i < kNumClasses; ++i) {
central_cache_[i].Init(i);
}
SetupAtForkLocksHandler();
// It's important to have PageHeap allocated, not in static storage,
// so that HeapLeakChecker does not consider all the byte patterns stored
// in is caches as pointers that are sources of heap object liveness,