mirror of https://github.com/mongodb/mongo
SERVER-19549 clang thread safety annotations (#43219)
Co-authored-by: Mathias Stearn <mathias@mongodb.com> GitOrigin-RevId: 71441319c7613ba0ea22ddae772c31cf1430d592
This commit is contained in:
parent
d545ba6fe4
commit
3cf541e9ef
|
|
@ -2714,6 +2714,9 @@ WORKSPACE.bazel @10gen/devprod-build @svc-auto-approve-bot
|
|||
/src/mongo/db/repl/**/*topology_coordinator* @10gen/server-replication-coordinator @svc-auto-approve-bot
|
||||
/src/mongo/db/repl/**/FCV_AND_FEATURE_FLAG_README.md @10gen/server-catalog-and-routing-routing-and-topology @10gen/server-fcv @svc-auto-approve-bot
|
||||
|
||||
# The following patterns are parsed from ./src/mongo/db/repl/clang_checked/OWNERS.yml
|
||||
/src/mongo/db/repl/clang_checked/**/* @10gen/server-replication-reviewers @svc-auto-approve-bot
|
||||
|
||||
# The following patterns are parsed from ./src/mongo/db/repl/dbcheck/OWNERS.yml
|
||||
/src/mongo/db/repl/dbcheck/**/* @10gen/server-dbcheck @svc-auto-approve-bot
|
||||
|
||||
|
|
|
|||
|
|
@ -231,6 +231,7 @@ SYMBOL_ORDER_FILES = [
|
|||
RE_ENABLE_DISABLED_3RD_PARTY_WARNINGS_FEATURES = select({
|
||||
"//bazel/config:compiler_type_clang": [
|
||||
"-disable_warnings_for_third_party_libraries_clang",
|
||||
"thread_safety_warnings",
|
||||
],
|
||||
"//bazel/config:compiler_type_gcc": [
|
||||
"-disable_warnings_for_third_party_libraries_gcc",
|
||||
|
|
|
|||
|
|
@ -784,6 +784,20 @@ def _impl(ctx):
|
|||
],
|
||||
)
|
||||
|
||||
thread_safety_warnings_feature = feature(
|
||||
name = "thread_safety_warnings",
|
||||
enabled = False,
|
||||
flag_sets = [
|
||||
flag_set(
|
||||
actions = all_cpp_compile_actions,
|
||||
flag_groups = [flag_group(flags = [
|
||||
# Warn about thread safety issues
|
||||
"-Wthread-safety",
|
||||
])],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
disable_warnings_for_third_party_libraries_clang_feature = feature(
|
||||
name = "disable_warnings_for_third_party_libraries_clang",
|
||||
enabled = ctx.attr.compiler == COMPILERS.CLANG,
|
||||
|
|
@ -1418,6 +1432,7 @@ def _impl(ctx):
|
|||
pessimizing_move_warning_feature,
|
||||
no_class_memaccess_warning_feature,
|
||||
no_interference_size_warning_feature,
|
||||
thread_safety_warnings_feature,
|
||||
disable_warnings_for_third_party_libraries_clang_feature,
|
||||
disable_warnings_for_third_party_libraries_gcc_feature,
|
||||
disable_floating_point_contractions_feature,
|
||||
|
|
|
|||
|
|
@ -985,6 +985,14 @@ replicated_storage_service:
|
|||
files:
|
||||
- src/mongo/db/rss
|
||||
|
||||
replication.clang_checked:
|
||||
meta:
|
||||
slack: server-replication
|
||||
jira: Replication
|
||||
fully_marked: true
|
||||
files:
|
||||
- src/mongo/db/repl/clang_checked/*
|
||||
|
||||
replication.hello:
|
||||
meta:
|
||||
slack: server-replication
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
load("//bazel:mongo_src_rules.bzl", "mongo_cc_library", "mongo_cc_unit_test")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
exports_files(glob(["*.h"]))
|
||||
|
||||
mongo_cc_library(
|
||||
name = "clang_checked",
|
||||
hdrs = [
|
||||
"checked_mutex.h",
|
||||
"lockable_concepts.h",
|
||||
"mutex.h",
|
||||
"thread_safety_annotations.h",
|
||||
],
|
||||
)
|
||||
|
||||
mongo_cc_unit_test(
|
||||
name = "clang_checked_test",
|
||||
srcs = [
|
||||
"checked_mutex_test.cpp",
|
||||
"mutex_test.cpp",
|
||||
],
|
||||
tags = [
|
||||
"mongo_unittest_second_group",
|
||||
"server-programmability",
|
||||
],
|
||||
deps = [
|
||||
"//src/mongo/db/repl/clang_checked",
|
||||
],
|
||||
)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
version: 1.0.0
|
||||
filters:
|
||||
- "*":
|
||||
approvers:
|
||||
- 10gen/server-replication-reviewers
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* Copyright (C) 2025-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/db/repl/clang_checked/lockable_concepts.h"
|
||||
#include "mongo/db/repl/clang_checked/thread_safety_annotations.h"
|
||||
#include "mongo/util/modules.h"
|
||||
|
||||
#include <concepts>
|
||||
|
||||
namespace MONGO_MOD_PUB mongo {
|
||||
namespace clang_checked {
|
||||
|
||||
/**
|
||||
* CheckedMutex is a wrapper for other mutex types for cases where clang's
|
||||
* thread safety checks are desirable. By combining CheckedMutex and the
|
||||
* annotations defined in thread_safety_annotations.h in this directory, you
|
||||
* can enforce constraints such as "this member is always guarded by that
|
||||
* mutex."
|
||||
*/
|
||||
template <BaseLockable MutexT>
|
||||
class MONGO_LOCKING_CAPABILITY("mutex") CheckedMutex {
|
||||
public:
|
||||
using mutex_type = MutexT;
|
||||
|
||||
void lock() MONGO_LOCKING_ACQUIRE() {
|
||||
_m.lock();
|
||||
}
|
||||
|
||||
bool try_lock()
|
||||
requires TryLockable<mutex_type>
|
||||
MONGO_LOCKING_TRY_ACQUIRE(true) {
|
||||
return _m.try_lock();
|
||||
}
|
||||
|
||||
void unlock() MONGO_LOCKING_RELEASE() {
|
||||
_m.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
mutable mutex_type _m;
|
||||
};
|
||||
|
||||
} // namespace clang_checked
|
||||
} // namespace MONGO_MOD_PUB mongo
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* Copyright (C) 2025-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/db/repl/clang_checked/checked_mutex.h"
|
||||
|
||||
#include "mongo/db/repl/clang_checked/mutex.h"
|
||||
#include "mongo/db/repl/clang_checked/thread_safety_annotations.h"
|
||||
#include "mongo/stdx/mutex.h"
|
||||
#include "mongo/unittest/unittest.h"
|
||||
#include "mongo/util/observable_mutex.h"
|
||||
|
||||
namespace mongo {
|
||||
namespace {
|
||||
|
||||
template <typename MutexT>
|
||||
class Foo {
|
||||
public:
|
||||
using mutex_type = MutexT;
|
||||
|
||||
explicit Foo(int i) : _i(i) {}
|
||||
|
||||
void incI() {
|
||||
clang_checked::lock_guard lk(_m);
|
||||
_i++;
|
||||
}
|
||||
|
||||
int getI() {
|
||||
clang_checked::lock_guard lk(_m);
|
||||
return _i;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable clang_checked::CheckedMutex<mutex_type> _m;
|
||||
|
||||
int _i MONGO_LOCKING_GUARDED_BY(_m);
|
||||
};
|
||||
|
||||
TEST(CheckedMutexTest, CheckedMutexShouldWorkWithStdxMutex) {
|
||||
auto f = Foo<stdx::mutex>(0);
|
||||
ASSERT_EQ(f.getI(), 0);
|
||||
f.incI();
|
||||
ASSERT_EQ(f.getI(), 1);
|
||||
}
|
||||
|
||||
TEST(CheckedMutexTest, CheckedMutexShouldWorkWithObservableMutexStdxMutex) {
|
||||
auto f = Foo<ObservableMutex<mongo::stdx::mutex>>(0);
|
||||
ASSERT_EQ(f.getI(), 0);
|
||||
f.incI();
|
||||
ASSERT_EQ(f.getI(), 1);
|
||||
}
|
||||
} // namespace
|
||||
} // namespace mongo
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Copyright (C) 2025-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/util/modules.h"
|
||||
|
||||
#include <concepts>
|
||||
|
||||
namespace MONGO_MOD_PUB mongo {
|
||||
namespace clang_checked {
|
||||
|
||||
template <typename T>
|
||||
concept BaseLockable = requires(T obj) {
|
||||
{ obj.lock() };
|
||||
{ obj.unlock() };
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept TryLockable = requires(T obj) {
|
||||
requires BaseLockable<T>;
|
||||
{ obj.try_lock() };
|
||||
};
|
||||
|
||||
} // namespace clang_checked
|
||||
} // namespace MONGO_MOD_PUB mongo
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
/**
|
||||
* Copyright (C) 2025-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/db/repl/clang_checked/lockable_concepts.h"
|
||||
#include "mongo/db/repl/clang_checked/thread_safety_annotations.h"
|
||||
#include "mongo/util/concurrency/with_lock.h"
|
||||
#include "mongo/util/modules.h"
|
||||
|
||||
#include <system_error>
|
||||
|
||||
namespace MONGO_MOD_PUB mongo {
|
||||
namespace clang_checked {
|
||||
|
||||
struct adopt_lock_t {
|
||||
explicit adopt_lock_t() = default;
|
||||
};
|
||||
constexpr inline adopt_lock_t adopt_lock;
|
||||
|
||||
struct defer_lock_t {
|
||||
explicit defer_lock_t() = default;
|
||||
};
|
||||
constexpr inline defer_lock_t defer_lock;
|
||||
|
||||
struct try_to_lock_t {
|
||||
explicit try_to_lock_t() = default;
|
||||
};
|
||||
constexpr inline try_to_lock_t try_to_lock;
|
||||
|
||||
/**
|
||||
* lock_guard is analagous to std::lock_guard and is compatible with
|
||||
* CheckedMutex defined in checked_mutex.h.
|
||||
*/
|
||||
template <BaseLockable MutexT>
|
||||
class MONGO_LOCKING_SCOPED_CAPABILITY lock_guard {
|
||||
public:
|
||||
using mutex_type = MutexT;
|
||||
|
||||
explicit lock_guard(mutex_type& mu) MONGO_LOCKING_ACQUIRE(mu) : _mu(mu) {
|
||||
_mu.lock();
|
||||
}
|
||||
|
||||
lock_guard(mutex_type& mu, adopt_lock_t) MONGO_LOCKING_ACQUIRE(mu) : _mu(mu) {}
|
||||
|
||||
lock_guard(lock_guard&&) = delete;
|
||||
lock_guard& operator=(lock_guard&&) = delete;
|
||||
|
||||
~lock_guard() MONGO_LOCKING_RELEASE() {
|
||||
_mu.unlock();
|
||||
}
|
||||
|
||||
operator WithLock() {
|
||||
// We know that we hold the lock.
|
||||
return WithLock::withoutLock();
|
||||
}
|
||||
|
||||
private:
|
||||
mutex_type& _mu;
|
||||
};
|
||||
|
||||
/**
|
||||
* unique_lock is analagous to std::unique_lock and is compatible with
|
||||
* CheckedMutex defined in checked_mutex.h.
|
||||
*/
|
||||
template <BaseLockable MutexT>
|
||||
class MONGO_LOCKING_SCOPED_CAPABILITY unique_lock {
|
||||
public:
|
||||
using mutex_type = MutexT;
|
||||
|
||||
unique_lock() noexcept : _mu(nullptr), _locked(true) {}
|
||||
|
||||
explicit unique_lock(mutex_type& mu) MONGO_LOCKING_ACQUIRE(mu) : _mu(&mu), _locked(true) {
|
||||
_mu->lock();
|
||||
}
|
||||
|
||||
unique_lock(mutex_type& mu, defer_lock_t) noexcept : _mu(&mu) {}
|
||||
|
||||
unique_lock(mutex_type& mu, try_to_lock_t) MONGO_LOCKING_TRY_ACQUIRE(true, mu) : _mu(&mu) {
|
||||
_locked = _mu->try_lock();
|
||||
}
|
||||
|
||||
unique_lock(mutex_type& mu, adopt_lock_t) MONGO_LOCKING_ACQUIRE(mu) : _mu(&mu), _locked(true) {
|
||||
invariant(_mu->owns_lock());
|
||||
}
|
||||
|
||||
unique_lock(unique_lock&& other) noexcept
|
||||
: _mu{std::exchange(other._mu, nullptr)}, _locked{std::exchange(other._locked, false)} {}
|
||||
|
||||
unique_lock& operator=(unique_lock&& other) noexcept MONGO_LOCKING_NO_THREAD_SAFETY_ANALYSIS {
|
||||
if (this != &other) {
|
||||
if (_locked) {
|
||||
_mu->unlock();
|
||||
}
|
||||
_mu = std::exchange(other._mu, nullptr);
|
||||
_locked = std::exchange(other._locked, false);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~unique_lock() MONGO_LOCKING_RELEASE() {
|
||||
if (_locked) {
|
||||
_mu->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void lock() MONGO_LOCKING_ACQUIRE(_mu) {
|
||||
assertHasMutex();
|
||||
_mu->lock();
|
||||
_locked = true;
|
||||
}
|
||||
|
||||
bool try_lock()
|
||||
requires TryLockable<mutex_type>
|
||||
MONGO_LOCKING_TRY_ACQUIRE(true, _mu) {
|
||||
assertHasMutex();
|
||||
if (_locked) {
|
||||
return false;
|
||||
}
|
||||
_locked = _mu->try_lock();
|
||||
return _locked;
|
||||
}
|
||||
|
||||
void unlock() MONGO_LOCKING_RELEASE() {
|
||||
assertHasMutex();
|
||||
_mu->unlock();
|
||||
_locked = false;
|
||||
}
|
||||
|
||||
bool owns_lock() const noexcept {
|
||||
return _locked;
|
||||
}
|
||||
|
||||
mutex_type* mutex() const noexcept {
|
||||
return _mu;
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return _locked;
|
||||
}
|
||||
|
||||
void swap(unique_lock& other) noexcept {
|
||||
using std::swap;
|
||||
swap(_mu, other._mu);
|
||||
swap(_locked, other._locked);
|
||||
}
|
||||
|
||||
friend void swap(unique_lock& lhs, unique_lock& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
operator WithLock() {
|
||||
invariant(owns_lock());
|
||||
return WithLock::withoutLock();
|
||||
}
|
||||
|
||||
private:
|
||||
void assertHasMutex() {
|
||||
if (MONGO_unlikely(_mu == nullptr)) {
|
||||
std::error_code ec = std::make_error_code(std::errc::operation_not_permitted);
|
||||
throw std::system_error(ec, "No mutex!");
|
||||
}
|
||||
}
|
||||
|
||||
mutable mutex_type* _mu;
|
||||
|
||||
bool _locked;
|
||||
};
|
||||
|
||||
} // namespace clang_checked
|
||||
} // namespace MONGO_MOD_PUB mongo
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
/**
|
||||
* Copyright (C) 2025-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/db/repl/clang_checked/mutex.h"
|
||||
|
||||
#include "mongo/unittest/unittest.h"
|
||||
|
||||
namespace mongo {
|
||||
namespace {
|
||||
TEST(MutexTest, LockGuardShouldLock) {
|
||||
stdx::mutex m;
|
||||
clang_checked::lock_guard lk(m);
|
||||
// If lock_guard locked m, then try_lock() should return false.
|
||||
ASSERT_FALSE(m.try_lock());
|
||||
}
|
||||
|
||||
TEST(MutexTest, UniqueLockShouldLockAndUnlock) {
|
||||
stdx::mutex m;
|
||||
clang_checked::unique_lock lk(m);
|
||||
ASSERT_TRUE(lk.owns_lock());
|
||||
lk.unlock();
|
||||
ASSERT_FALSE(lk.owns_lock());
|
||||
}
|
||||
|
||||
TEST(MutexTest, UniqueLockShouldTryLockAndOwnsLock) MONGO_LOCKING_NO_THREAD_SAFETY_ANALYSIS {
|
||||
stdx::mutex m;
|
||||
clang_checked::unique_lock lk(m);
|
||||
ASSERT_FALSE(lk.try_lock());
|
||||
ASSERT_TRUE(lk.owns_lock());
|
||||
lk.unlock();
|
||||
ASSERT_FALSE(lk.owns_lock());
|
||||
ASSERT_TRUE(lk.try_lock());
|
||||
ASSERT_TRUE(lk.owns_lock());
|
||||
lk.unlock();
|
||||
ASSERT_FALSE(lk.owns_lock());
|
||||
}
|
||||
|
||||
TEST(MutexTest, UniqueLockShouldConvertToBoolean) MONGO_LOCKING_NO_THREAD_SAFETY_ANALYSIS {
|
||||
stdx::mutex m;
|
||||
clang_checked::unique_lock lk(m);
|
||||
ASSERT_FALSE(lk.try_lock());
|
||||
ASSERT_TRUE(lk);
|
||||
lk.unlock();
|
||||
ASSERT_FALSE(lk);
|
||||
ASSERT_TRUE(lk.try_lock());
|
||||
ASSERT_TRUE(lk);
|
||||
lk.unlock();
|
||||
ASSERT_FALSE(lk);
|
||||
}
|
||||
|
||||
class MONGO_LOCKING_SCOPED_CAPABILITY MutexWithId {
|
||||
public:
|
||||
explicit MutexWithId(int id) : _id(id) {}
|
||||
|
||||
void lock() MONGO_LOCKING_ACQUIRE() {}
|
||||
|
||||
void unlock() MONGO_LOCKING_RELEASE() {}
|
||||
|
||||
int id() const {
|
||||
return _id;
|
||||
}
|
||||
|
||||
private:
|
||||
int _id;
|
||||
};
|
||||
|
||||
TEST(MutexTest, UniqueLockShouldReturnMutex) MONGO_LOCKING_NO_THREAD_SAFETY_ANALYSIS {
|
||||
MutexWithId m(3);
|
||||
clang_checked::unique_lock lk(m);
|
||||
ASSERT_EQ(lk.mutex()->id(), m.id());
|
||||
lk.lock();
|
||||
ASSERT_EQ(lk.mutex()->id(), m.id());
|
||||
lk.unlock();
|
||||
ASSERT_EQ(lk.mutex()->id(), m.id());
|
||||
}
|
||||
|
||||
TEST(MutexTest, UniqueLockCanSwapWhenBothLocked) MONGO_LOCKING_NO_THREAD_SAFETY_ANALYSIS {
|
||||
MutexWithId m0(0);
|
||||
clang_checked::unique_lock lk0(m0);
|
||||
MutexWithId m1(1);
|
||||
clang_checked::unique_lock lk1(m1);
|
||||
ASSERT_EQ(lk0.mutex()->id(), m0.id());
|
||||
ASSERT_EQ(lk1.mutex()->id(), m1.id());
|
||||
ASSERT_TRUE(lk0.owns_lock());
|
||||
ASSERT_TRUE(lk1.owns_lock());
|
||||
lk0.swap(lk1);
|
||||
ASSERT_EQ(lk1.mutex()->id(), m0.id());
|
||||
ASSERT_EQ(lk0.mutex()->id(), m1.id());
|
||||
ASSERT_TRUE(lk0.owns_lock());
|
||||
ASSERT_TRUE(lk1.owns_lock());
|
||||
}
|
||||
|
||||
TEST(MutexTest, UniqueLockCanSwapWhenNeitherLocked) MONGO_LOCKING_NO_THREAD_SAFETY_ANALYSIS {
|
||||
MutexWithId m0(0);
|
||||
clang_checked::unique_lock lk0(m0);
|
||||
lk0.unlock();
|
||||
MutexWithId m1(1);
|
||||
clang_checked::unique_lock lk1(m1);
|
||||
lk1.unlock();
|
||||
ASSERT_EQ(lk0.mutex()->id(), m0.id());
|
||||
ASSERT_EQ(lk1.mutex()->id(), m1.id());
|
||||
ASSERT_FALSE(lk0.owns_lock());
|
||||
ASSERT_FALSE(lk1.owns_lock());
|
||||
lk0.swap(lk1);
|
||||
ASSERT_EQ(lk1.mutex()->id(), m0.id());
|
||||
ASSERT_EQ(lk0.mutex()->id(), m1.id());
|
||||
ASSERT_FALSE(lk0.owns_lock());
|
||||
ASSERT_FALSE(lk1.owns_lock());
|
||||
}
|
||||
|
||||
TEST(MutexTest, UniqueLockCanSwapWhenFirstLockedSecondUnlocked)
|
||||
MONGO_LOCKING_NO_THREAD_SAFETY_ANALYSIS {
|
||||
MutexWithId m0(0);
|
||||
clang_checked::unique_lock lk0(m0);
|
||||
MutexWithId m1(1);
|
||||
clang_checked::unique_lock lk1(m1);
|
||||
lk1.unlock();
|
||||
ASSERT_EQ(lk0.mutex()->id(), m0.id());
|
||||
ASSERT_EQ(lk1.mutex()->id(), m1.id());
|
||||
ASSERT_TRUE(lk0.owns_lock());
|
||||
ASSERT_FALSE(lk1.owns_lock());
|
||||
lk0.swap(lk1);
|
||||
ASSERT_EQ(lk1.mutex()->id(), m0.id());
|
||||
ASSERT_EQ(lk0.mutex()->id(), m1.id());
|
||||
ASSERT_FALSE(lk0.owns_lock());
|
||||
ASSERT_TRUE(lk1.owns_lock());
|
||||
}
|
||||
|
||||
TEST(MutexTest, UniqueLockCanSwapWhenFirstUnlockedSecondLocked)
|
||||
MONGO_LOCKING_NO_THREAD_SAFETY_ANALYSIS {
|
||||
MutexWithId m0(0);
|
||||
clang_checked::unique_lock lk0(m0);
|
||||
lk0.unlock();
|
||||
MutexWithId m1(1);
|
||||
clang_checked::unique_lock lk1(m1);
|
||||
ASSERT_EQ(lk0.mutex()->id(), m0.id());
|
||||
ASSERT_EQ(lk1.mutex()->id(), m1.id());
|
||||
ASSERT_FALSE(lk0.owns_lock());
|
||||
ASSERT_TRUE(lk1.owns_lock());
|
||||
lk0.swap(lk1);
|
||||
ASSERT_EQ(lk1.mutex()->id(), m0.id());
|
||||
ASSERT_EQ(lk0.mutex()->id(), m1.id());
|
||||
ASSERT_TRUE(lk0.owns_lock());
|
||||
ASSERT_FALSE(lk1.owns_lock());
|
||||
}
|
||||
|
||||
void takesWithLock(WithLock) {}
|
||||
|
||||
TEST(MutexTest, LockGuardShouldConvertToWithLock) {
|
||||
stdx::mutex m;
|
||||
clang_checked::lock_guard lk(m);
|
||||
takesWithLock(lk);
|
||||
}
|
||||
|
||||
TEST(MutexTest, UniqueLockShouldConvertToWithLock) {
|
||||
stdx::mutex m;
|
||||
clang_checked::unique_lock lk(m);
|
||||
takesWithLock(lk);
|
||||
}
|
||||
} // namespace
|
||||
} // namespace mongo
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/**
|
||||
* Copyright (C) 2025-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* This is heavily inspired by https://clang.llvm.org/docs/ThreadSafetyAnalysis.html.
|
||||
*
|
||||
* To enforce a modicum of thread safety at compile time, we use the clang
|
||||
* thread safety annotations. Mutex and mutex-like classes along with RAII-
|
||||
* style guards must be annotated as such.
|
||||
*
|
||||
* When an error results from clang's failure to prove that a code block is
|
||||
* safe, or when you do something unsafe on purpose, you can use the
|
||||
* MONGO_LOCKING_NO_THREAD_SAFETY_ANALYSIS annotation. There are examples
|
||||
* throughout the codebase.
|
||||
*/
|
||||
|
||||
// Enable thread safety attributes only with clang.
|
||||
// The attributes can be safely erased when compiling with other compilers.
|
||||
#if defined(__clang__)
|
||||
#define MONGO_LOCKING_ATTR(x) __attribute__((x))
|
||||
#else
|
||||
#define MONGO_LOCKING_ATTR(x)
|
||||
#endif
|
||||
|
||||
#define MONGO_LOCKING_CAPABILITY(x) MONGO_LOCKING_ATTR(capability(x))
|
||||
|
||||
#define MONGO_LOCKING_REENTRANT_CAPABILITY MONGO_LOCKING_ATTR(reentrant_capability)
|
||||
|
||||
#define MONGO_LOCKING_SCOPED_CAPABILITY MONGO_LOCKING_ATTR(scoped_lockable)
|
||||
|
||||
#define MONGO_LOCKING_GUARDED_BY(x) MONGO_LOCKING_ATTR(guarded_by(x))
|
||||
|
||||
#define MONGO_LOCKING_PT_GUARDED_BY(x) MONGO_LOCKING_ATTR(pt_guarded_by(x))
|
||||
|
||||
#define MONGO_LOCKING_ACQUIRED_BEFORE(...) MONGO_LOCKING_ATTR(acquired_before(__VA_ARGS__))
|
||||
|
||||
#define MONGO_LOCKING_ACQUIRED_AFTER(...) MONGO_LOCKING_ATTR(acquired_after(__VA_ARGS__))
|
||||
|
||||
#define MONGO_LOCKING_REQUIRES(...) MONGO_LOCKING_ATTR(requires_capability(__VA_ARGS__))
|
||||
|
||||
#define MONGO_LOCKING_REQUIRES_SHARED(...) \
|
||||
MONGO_LOCKING_ATTR(requires_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define MONGO_LOCKING_ACQUIRE(...) MONGO_LOCKING_ATTR(acquire_capability(__VA_ARGS__))
|
||||
|
||||
#define MONGO_LOCKING_ACQUIRE_SHARED(...) MONGO_LOCKING_ATTR(acquire_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define MONGO_LOCKING_RELEASE(...) MONGO_LOCKING_ATTR(release_capability(__VA_ARGS__))
|
||||
|
||||
#define MONGO_LOCKING_RELEASE_SHARED(...) MONGO_LOCKING_ATTR(release_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define MONGO_LOCKING_RELEASE_GENERIC(...) \
|
||||
MONGO_LOCKING_ATTR(release_generic_capability(__VA_ARGS__))
|
||||
|
||||
#define MONGO_LOCKING_TRY_ACQUIRE(...) MONGO_LOCKING_ATTR(try_acquire_capability(__VA_ARGS__))
|
||||
|
||||
#define MONGO_LOCKING_TRY_ACQUIRE_SHARED(...) \
|
||||
MONGO_LOCKING_ATTR(try_acquire_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define MONGO_LOCKING_EXCLUDES(...) MONGO_LOCKING_ATTR(locks_excluded(__VA_ARGS__))
|
||||
|
||||
#define MONGO_LOCKING_ASSERT_CAPABILITY(x) MONGO_LOCKING_ATTR(assert_capability(x))
|
||||
|
||||
#define MONGO_LOCKING_ASSERT_SHARED_CAPABILITY(x) MONGO_LOCKING_ATTR(assert_shared_capability(x))
|
||||
|
||||
#define MONGO_LOCKING_RETURN_CAPABILITY(x) MONGO_LOCKING_ATTR(lock_returned(x))
|
||||
|
||||
#define MONGO_LOCKING_NO_THREAD_SAFETY_ANALYSIS MONGO_LOCKING_ATTR(no_thread_safety_analysis)
|
||||
Loading…
Reference in New Issue