Skip to content

Commit ab17ecd

Browse files
authored
[scudo] Add ConditionVariable in SizeClassAllocator64 (#69031)
This may improve the waiting of `Region->MMLock` while trying to refill the freelist. Instead of always waiting on the completion of `populateFreeListAndPopBatch()` or `releaseToOSMaybe()`, `pushBlocks()` also refills the freelist. This increases the chance of earlier return from `popBatches()`. The support of condition variable hasn't been done for all platforms. Therefore, add another `popBatchWithCV()` and it can be configured in the allocator configuration by setting `Primary::UseConditionVariable` and the desired `ConditionVariableT`. Reviewed By: cferris Differential Revision: https://reviews.llvm.org/D156146
1 parent ff21a90 commit ab17ecd

11 files changed

+476
-21
lines changed

compiler-rt/lib/scudo/standalone/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ set(SCUDO_HEADERS
6262
bytemap.h
6363
checksum.h
6464
chunk.h
65+
condition_variable.h
66+
condition_variable_base.h
67+
condition_variable_linux.h
6568
combined.h
6669
common.h
6770
flags_parser.h
@@ -104,6 +107,7 @@ set(SCUDO_HEADERS
104107
set(SCUDO_SOURCES
105108
checksum.cpp
106109
common.cpp
110+
condition_variable_linux.cpp
107111
crc32_hw.cpp
108112
flags_parser.cpp
109113
flags.cpp

compiler-rt/lib/scudo/standalone/allocator_config.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "combined.h"
1313
#include "common.h"
14+
#include "condition_variable.h"
1415
#include "flags.h"
1516
#include "primary32.h"
1617
#include "primary64.h"
@@ -82,6 +83,14 @@ namespace scudo {
8283
// // Defines the minimal & maximal release interval that can be set.
8384
// static const s32 MinReleaseToOsIntervalMs = INT32_MIN;
8485
// static const s32 MaxReleaseToOsIntervalMs = INT32_MAX;
86+
//
87+
// // Use condition variable to shorten the waiting time of refillment of
88+
// // freelist. Note that this depends on the implementation of condition
89+
// // variable on each platform and the performance may vary so that it
90+
// // doesn't guarantee a performance benefit.
91+
// // Note that both variables have to be defined to enable it.
92+
// static const bool UseConditionVariable = true;
93+
// using ConditionVariableT = ConditionVariableLinux;
8594
// };
8695
// // Defines the type of Primary allocator to use.
8796
// template <typename Config> using PrimaryT = SizeClassAllocator64<Config>;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//===-- condition_variable.h ------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef SCUDO_CONDITION_VARIABLE_H_
10+
#define SCUDO_CONDITION_VARIABLE_H_
11+
12+
#include "condition_variable_base.h"
13+
14+
#include "common.h"
15+
#include "platform.h"
16+
17+
#include "condition_variable_linux.h"
18+
19+
namespace scudo {
20+
21+
// A default implementation of default condition variable. It doesn't do a real
22+
// `wait`, instead it spins a short amount of time only.
23+
class ConditionVariableDummy
24+
: public ConditionVariableBase<ConditionVariableDummy> {
25+
public:
26+
void notifyAllImpl(UNUSED HybridMutex &M) REQUIRES(M) {}
27+
28+
void waitImpl(UNUSED HybridMutex &M) REQUIRES(M) {
29+
M.unlock();
30+
31+
constexpr u32 SpinTimes = 64;
32+
volatile u32 V = 0;
33+
for (u32 I = 0; I < SpinTimes; ++I) {
34+
u32 Tmp = V + 1;
35+
V = Tmp;
36+
}
37+
38+
M.lock();
39+
}
40+
};
41+
42+
template <typename Config, typename = const bool>
43+
struct ConditionVariableState {
44+
static constexpr bool enabled() { return false; }
45+
// This is only used for compilation purpose so that we won't end up having
46+
// many conditional compilations. If you want to use `ConditionVariableDummy`,
47+
// define `ConditionVariableT` in your allocator configuration. See
48+
// allocator_config.h for more details.
49+
using ConditionVariableT = ConditionVariableDummy;
50+
};
51+
52+
template <typename Config>
53+
struct ConditionVariableState<Config, decltype(Config::UseConditionVariable)> {
54+
static constexpr bool enabled() { return true; }
55+
using ConditionVariableT = typename Config::ConditionVariableT;
56+
};
57+
58+
} // namespace scudo
59+
60+
#endif // SCUDO_CONDITION_VARIABLE_H_
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//===-- condition_variable_base.h -------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef SCUDO_CONDITION_VARIABLE_BASE_H_
10+
#define SCUDO_CONDITION_VARIABLE_BASE_H_
11+
12+
#include "mutex.h"
13+
#include "thread_annotations.h"
14+
15+
namespace scudo {
16+
17+
template <typename Derived> class ConditionVariableBase {
18+
public:
19+
constexpr ConditionVariableBase() = default;
20+
21+
void bindTestOnly(HybridMutex &Mutex) {
22+
#if SCUDO_DEBUG
23+
boundMutex = &Mutex;
24+
#else
25+
(void)Mutex;
26+
#endif
27+
}
28+
29+
void notifyAll(HybridMutex &M) REQUIRES(M) {
30+
#if SCUDO_DEBUG
31+
CHECK_EQ(&M, boundMutex);
32+
#endif
33+
getDerived()->notifyAllImpl(M);
34+
}
35+
36+
void wait(HybridMutex &M) REQUIRES(M) {
37+
#if SCUDO_DEBUG
38+
CHECK_EQ(&M, boundMutex);
39+
#endif
40+
getDerived()->waitImpl(M);
41+
}
42+
43+
protected:
44+
Derived *getDerived() { return static_cast<Derived *>(this); }
45+
46+
#if SCUDO_DEBUG
47+
// Because thread-safety analysis doesn't support pointer aliasing, we are not
48+
// able to mark the proper annotations without false positive. Instead, we
49+
// pass the lock and do the same-lock check separately.
50+
HybridMutex *boundMutex = nullptr;
51+
#endif
52+
};
53+
54+
} // namespace scudo
55+
56+
#endif // SCUDO_CONDITION_VARIABLE_BASE_H_
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//===-- condition_variable_linux.cpp ----------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "platform.h"
10+
11+
#if SCUDO_LINUX
12+
13+
#include "condition_variable_linux.h"
14+
15+
#include "atomic_helpers.h"
16+
17+
#include <limits.h>
18+
#include <linux/futex.h>
19+
#include <sys/syscall.h>
20+
#include <unistd.h>
21+
22+
namespace scudo {
23+
24+
void ConditionVariableLinux::notifyAllImpl(UNUSED HybridMutex &M) {
25+
const u32 V = atomic_load_relaxed(&Counter);
26+
atomic_store_relaxed(&Counter, V + 1);
27+
28+
// TODO(chiahungduan): Move the waiters from the futex waiting queue
29+
// `Counter` to futex waiting queue `M` so that the awoken threads won't be
30+
// blocked again due to locked `M` by current thread.
31+
if (LastNotifyAll != V) {
32+
syscall(SYS_futex, reinterpret_cast<uptr>(&Counter), FUTEX_WAKE_PRIVATE,
33+
INT_MAX, nullptr, nullptr, 0);
34+
}
35+
36+
LastNotifyAll = V + 1;
37+
}
38+
39+
void ConditionVariableLinux::waitImpl(HybridMutex &M) {
40+
const u32 V = atomic_load_relaxed(&Counter) + 1;
41+
atomic_store_relaxed(&Counter, V);
42+
43+
// TODO: Use ScopedUnlock when it's supported.
44+
M.unlock();
45+
syscall(SYS_futex, reinterpret_cast<uptr>(&Counter), FUTEX_WAIT_PRIVATE, V,
46+
nullptr, nullptr, 0);
47+
M.lock();
48+
}
49+
50+
} // namespace scudo
51+
52+
#endif // SCUDO_LINUX
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===-- condition_variable_linux.h ------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef SCUDO_CONDITION_VARIABLE_LINUX_H_
10+
#define SCUDO_CONDITION_VARIABLE_LINUX_H_
11+
12+
#include "platform.h"
13+
14+
#if SCUDO_LINUX
15+
16+
#include "atomic_helpers.h"
17+
#include "condition_variable_base.h"
18+
#include "thread_annotations.h"
19+
20+
namespace scudo {
21+
22+
class ConditionVariableLinux
23+
: public ConditionVariableBase<ConditionVariableLinux> {
24+
public:
25+
void notifyAllImpl(HybridMutex &M) REQUIRES(M);
26+
27+
void waitImpl(HybridMutex &M) REQUIRES(M);
28+
29+
private:
30+
u32 LastNotifyAll = 0;
31+
atomic_u32 Counter = {};
32+
};
33+
34+
} // namespace scudo
35+
36+
#endif // SCUDO_LINUX
37+
38+
#endif // SCUDO_CONDITION_VARIABLE_LINUX_H_

0 commit comments

Comments
 (0)