Skip to content

Commit

Permalink
[scudo] Add ConditionVariable in SizeClassAllocator64 (llvm#69031)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ChiaHungDuan authored Oct 19, 2023
1 parent ff21a90 commit ab17ecd
Show file tree
Hide file tree
Showing 11 changed files with 476 additions and 21 deletions.
4 changes: 4 additions & 0 deletions compiler-rt/lib/scudo/standalone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ set(SCUDO_HEADERS
bytemap.h
checksum.h
chunk.h
condition_variable.h
condition_variable_base.h
condition_variable_linux.h
combined.h
common.h
flags_parser.h
Expand Down Expand Up @@ -104,6 +107,7 @@ set(SCUDO_HEADERS
set(SCUDO_SOURCES
checksum.cpp
common.cpp
condition_variable_linux.cpp
crc32_hw.cpp
flags_parser.cpp
flags.cpp
Expand Down
9 changes: 9 additions & 0 deletions compiler-rt/lib/scudo/standalone/allocator_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "combined.h"
#include "common.h"
#include "condition_variable.h"
#include "flags.h"
#include "primary32.h"
#include "primary64.h"
Expand Down Expand Up @@ -82,6 +83,14 @@ namespace scudo {
// // Defines the minimal & maximal release interval that can be set.
// static const s32 MinReleaseToOsIntervalMs = INT32_MIN;
// static const s32 MaxReleaseToOsIntervalMs = INT32_MAX;
//
// // Use condition variable to shorten the waiting time of refillment of
// // freelist. Note that this depends on the implementation of condition
// // variable on each platform and the performance may vary so that it
// // doesn't guarantee a performance benefit.
// // Note that both variables have to be defined to enable it.
// static const bool UseConditionVariable = true;
// using ConditionVariableT = ConditionVariableLinux;
// };
// // Defines the type of Primary allocator to use.
// template <typename Config> using PrimaryT = SizeClassAllocator64<Config>;
Expand Down
60 changes: 60 additions & 0 deletions compiler-rt/lib/scudo/standalone/condition_variable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//===-- condition_variable.h ------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef SCUDO_CONDITION_VARIABLE_H_
#define SCUDO_CONDITION_VARIABLE_H_

#include "condition_variable_base.h"

#include "common.h"
#include "platform.h"

#include "condition_variable_linux.h"

namespace scudo {

// A default implementation of default condition variable. It doesn't do a real
// `wait`, instead it spins a short amount of time only.
class ConditionVariableDummy
: public ConditionVariableBase<ConditionVariableDummy> {
public:
void notifyAllImpl(UNUSED HybridMutex &M) REQUIRES(M) {}

void waitImpl(UNUSED HybridMutex &M) REQUIRES(M) {
M.unlock();

constexpr u32 SpinTimes = 64;
volatile u32 V = 0;
for (u32 I = 0; I < SpinTimes; ++I) {
u32 Tmp = V + 1;
V = Tmp;
}

M.lock();
}
};

template <typename Config, typename = const bool>
struct ConditionVariableState {
static constexpr bool enabled() { return false; }
// This is only used for compilation purpose so that we won't end up having
// many conditional compilations. If you want to use `ConditionVariableDummy`,
// define `ConditionVariableT` in your allocator configuration. See
// allocator_config.h for more details.
using ConditionVariableT = ConditionVariableDummy;
};

template <typename Config>
struct ConditionVariableState<Config, decltype(Config::UseConditionVariable)> {
static constexpr bool enabled() { return true; }
using ConditionVariableT = typename Config::ConditionVariableT;
};

} // namespace scudo

#endif // SCUDO_CONDITION_VARIABLE_H_
56 changes: 56 additions & 0 deletions compiler-rt/lib/scudo/standalone/condition_variable_base.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//===-- condition_variable_base.h -------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef SCUDO_CONDITION_VARIABLE_BASE_H_
#define SCUDO_CONDITION_VARIABLE_BASE_H_

#include "mutex.h"
#include "thread_annotations.h"

namespace scudo {

template <typename Derived> class ConditionVariableBase {
public:
constexpr ConditionVariableBase() = default;

void bindTestOnly(HybridMutex &Mutex) {
#if SCUDO_DEBUG
boundMutex = &Mutex;
#else
(void)Mutex;
#endif
}

void notifyAll(HybridMutex &M) REQUIRES(M) {
#if SCUDO_DEBUG
CHECK_EQ(&M, boundMutex);
#endif
getDerived()->notifyAllImpl(M);
}

void wait(HybridMutex &M) REQUIRES(M) {
#if SCUDO_DEBUG
CHECK_EQ(&M, boundMutex);
#endif
getDerived()->waitImpl(M);
}

protected:
Derived *getDerived() { return static_cast<Derived *>(this); }

#if SCUDO_DEBUG
// Because thread-safety analysis doesn't support pointer aliasing, we are not
// able to mark the proper annotations without false positive. Instead, we
// pass the lock and do the same-lock check separately.
HybridMutex *boundMutex = nullptr;
#endif
};

} // namespace scudo

#endif // SCUDO_CONDITION_VARIABLE_BASE_H_
52 changes: 52 additions & 0 deletions compiler-rt/lib/scudo/standalone/condition_variable_linux.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//===-- condition_variable_linux.cpp ----------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "platform.h"

#if SCUDO_LINUX

#include "condition_variable_linux.h"

#include "atomic_helpers.h"

#include <limits.h>
#include <linux/futex.h>
#include <sys/syscall.h>
#include <unistd.h>

namespace scudo {

void ConditionVariableLinux::notifyAllImpl(UNUSED HybridMutex &M) {
const u32 V = atomic_load_relaxed(&Counter);
atomic_store_relaxed(&Counter, V + 1);

// TODO(chiahungduan): Move the waiters from the futex waiting queue
// `Counter` to futex waiting queue `M` so that the awoken threads won't be
// blocked again due to locked `M` by current thread.
if (LastNotifyAll != V) {
syscall(SYS_futex, reinterpret_cast<uptr>(&Counter), FUTEX_WAKE_PRIVATE,
INT_MAX, nullptr, nullptr, 0);
}

LastNotifyAll = V + 1;
}

void ConditionVariableLinux::waitImpl(HybridMutex &M) {
const u32 V = atomic_load_relaxed(&Counter) + 1;
atomic_store_relaxed(&Counter, V);

// TODO: Use ScopedUnlock when it's supported.
M.unlock();
syscall(SYS_futex, reinterpret_cast<uptr>(&Counter), FUTEX_WAIT_PRIVATE, V,
nullptr, nullptr, 0);
M.lock();
}

} // namespace scudo

#endif // SCUDO_LINUX
38 changes: 38 additions & 0 deletions compiler-rt/lib/scudo/standalone/condition_variable_linux.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===-- condition_variable_linux.h ------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef SCUDO_CONDITION_VARIABLE_LINUX_H_
#define SCUDO_CONDITION_VARIABLE_LINUX_H_

#include "platform.h"

#if SCUDO_LINUX

#include "atomic_helpers.h"
#include "condition_variable_base.h"
#include "thread_annotations.h"

namespace scudo {

class ConditionVariableLinux
: public ConditionVariableBase<ConditionVariableLinux> {
public:
void notifyAllImpl(HybridMutex &M) REQUIRES(M);

void waitImpl(HybridMutex &M) REQUIRES(M);

private:
u32 LastNotifyAll = 0;
atomic_u32 Counter = {};
};

} // namespace scudo

#endif // SCUDO_LINUX

#endif // SCUDO_CONDITION_VARIABLE_LINUX_H_
Loading

0 comments on commit ab17ecd

Please sign in to comment.