Skip to content

Commit 3aa3377

Browse files
derekmaurocopybara-github
authored andcommitted
Fixed Windows DLL builds of test targets
This is a heavily modified version of #1445, which adds some missing test libraries to the test DLL. Unlike #1445, this change moves several global variables out of headers that did not need to be in headers. For instance, cord_btree_exhaustive_validation was a global defined/declared in cord_internal, but only used in cord_rep_btree and its test. cordz_handle defined a queue in its header even though it wasn't needed, which also led to ODR problems. The Spinlock used in CordzHandle is replaced with a Mutex. This was originally a Mutex, but Chromium asked us to change it to a Spinlock to avoid a static initializer. After this change, the static initializer is no longer an issue. #1407 PiperOrigin-RevId: 531516991 Change-Id: I0e431a193698b20ba03fac6e414c26f153f330a7
1 parent 2526926 commit 3aa3377

File tree

9 files changed

+79
-68
lines changed

9 files changed

+79
-68
lines changed

CMake/AbseilDll.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,10 @@ set(ABSL_INTERNAL_TEST_DLL_FILES
589589
"hash/hash_testing.h"
590590
"log/scoped_mock_log.cc"
591591
"log/scoped_mock_log.h"
592+
"random/internal/chi_square.cc"
593+
"random/internal/chi_square.h"
594+
"random/internal/distribution_test_util.cc"
595+
"random/internal/distribution_test_util.h"
592596
"random/internal/mock_helpers.h"
593597
"random/internal/mock_overload_set.h"
594598
"random/mocking_bit_gen.h"

CMake/AbseilHelpers.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,10 @@ function(absl_cc_test)
413413
DEPS ${ABSL_CC_TEST_DEPS}
414414
OUTPUT ABSL_CC_TEST_DEPS
415415
)
416+
absl_internal_dll_targets(
417+
DEPS ${ABSL_CC_TEST_LINKOPTS}
418+
OUTPUT ABSL_CC_TEST_LINKOPTS
419+
)
416420
else()
417421
target_compile_definitions(${_NAME}
418422
PUBLIC

absl/strings/internal/cord_internal.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ ABSL_CONST_INIT std::atomic<bool> cord_ring_buffer_enabled(
3333
kCordEnableRingBufferDefault);
3434
ABSL_CONST_INIT std::atomic<bool> shallow_subcords_enabled(
3535
kCordShallowSubcordsDefault);
36-
ABSL_CONST_INIT std::atomic<bool> cord_btree_exhaustive_validation(false);
3736

3837
void LogFatalNodeType(CordRep* rep) {
3938
ABSL_INTERNAL_LOG(FATAL, absl::StrCat("Unexpected node type: ",

absl/strings/internal/cord_internal.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,6 @@ enum CordFeatureDefaults {
6969
extern std::atomic<bool> cord_ring_buffer_enabled;
7070
extern std::atomic<bool> shallow_subcords_enabled;
7171

72-
// `cord_btree_exhaustive_validation` can be set to force exhaustive validation
73-
// in debug assertions, and code that calls `IsValid()` explicitly. By default,
74-
// assertions should be relatively cheap and AssertValid() can easily lead to
75-
// O(n^2) complexity as recursive / full tree validation is O(n).
76-
extern std::atomic<bool> cord_btree_exhaustive_validation;
77-
7872
inline void enable_cord_ring_buffer(bool enable) {
7973
cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed);
8074
}

absl/strings/internal/cord_rep_btree.cc

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "absl/strings/internal/cord_rep_btree.h"
1616

17+
#include <atomic>
1718
#include <cassert>
1819
#include <cstdint>
1920
#include <iostream>
@@ -49,9 +50,7 @@ using CopyResult = CordRepBtree::CopyResult;
4950
constexpr auto kFront = CordRepBtree::kFront;
5051
constexpr auto kBack = CordRepBtree::kBack;
5152

52-
inline bool exhaustive_validation() {
53-
return cord_btree_exhaustive_validation.load(std::memory_order_relaxed);
54-
}
53+
ABSL_CONST_INIT std::atomic<bool> cord_btree_exhaustive_validation(false);
5554

5655
// Implementation of the various 'Dump' functions.
5756
// Prints the entire tree structure or 'rep'. External callers should
@@ -362,6 +361,15 @@ struct StackOperations {
362361

363362
} // namespace
364363

364+
void SetCordBtreeExhaustiveValidation(bool do_exaustive_validation) {
365+
cord_btree_exhaustive_validation.store(do_exaustive_validation,
366+
std::memory_order_relaxed);
367+
}
368+
369+
bool IsCordBtreeExhaustiveValidationEnabled() {
370+
return cord_btree_exhaustive_validation.load(std::memory_order_relaxed);
371+
}
372+
365373
void CordRepBtree::Dump(const CordRep* rep, absl::string_view label,
366374
bool include_contents, std::ostream& stream) {
367375
stream << "===================================\n";
@@ -450,7 +458,8 @@ bool CordRepBtree::IsValid(const CordRepBtree* tree, bool shallow) {
450458
child_length += edge->length;
451459
}
452460
NODE_CHECK_EQ(child_length, tree->length);
453-
if ((!shallow || exhaustive_validation()) && tree->height() > 0) {
461+
if ((!shallow || IsCordBtreeExhaustiveValidationEnabled()) &&
462+
tree->height() > 0) {
454463
for (CordRep* edge : tree->Edges()) {
455464
if (!IsValid(edge->btree(), shallow)) return false;
456465
}

absl/strings/internal/cord_rep_btree.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ namespace absl {
3232
ABSL_NAMESPACE_BEGIN
3333
namespace cord_internal {
3434

35+
// `SetCordBtreeExhaustiveValidation()` can be set to force exhaustive
36+
// validation in debug assertions, and code that calls `IsValid()`
37+
// explicitly. By default, assertions should be relatively cheap and
38+
// AssertValid() can easily lead to O(n^2) complexity as recursive / full tree
39+
// validation is O(n).
40+
void SetCordBtreeExhaustiveValidation(bool do_exaustive_validation);
41+
bool IsCordBtreeExhaustiveValidationEnabled();
42+
3543
class CordRepBtreeNavigator;
3644

3745
// CordRepBtree is as the name implies a btree implementation of a Cordrep tree.

absl/strings/internal/cord_rep_btree_test.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,9 +1355,9 @@ TEST(CordRepBtreeTest, AssertValid) {
13551355

13561356
TEST(CordRepBtreeTest, CheckAssertValidShallowVsDeep) {
13571357
// Restore exhaustive validation on any exit.
1358-
const bool exhaustive_validation = cord_btree_exhaustive_validation.load();
1358+
const bool exhaustive_validation = IsCordBtreeExhaustiveValidationEnabled();
13591359
auto cleanup = absl::MakeCleanup([exhaustive_validation] {
1360-
cord_btree_exhaustive_validation.store(exhaustive_validation);
1360+
SetCordBtreeExhaustiveValidation(exhaustive_validation);
13611361
});
13621362

13631363
// Create a tree of at least 2 levels, and mess with the original flat, which
@@ -1372,7 +1372,7 @@ TEST(CordRepBtreeTest, CheckAssertValidShallowVsDeep) {
13721372
}
13731373
flat->length = 100;
13741374

1375-
cord_btree_exhaustive_validation.store(false);
1375+
SetCordBtreeExhaustiveValidation(false);
13761376
EXPECT_FALSE(CordRepBtree::IsValid(tree));
13771377
EXPECT_TRUE(CordRepBtree::IsValid(tree, true));
13781378
EXPECT_FALSE(CordRepBtree::IsValid(tree, false));
@@ -1382,7 +1382,7 @@ TEST(CordRepBtreeTest, CheckAssertValidShallowVsDeep) {
13821382
EXPECT_DEBUG_DEATH(CordRepBtree::AssertValid(tree, false), ".*");
13831383
#endif
13841384

1385-
cord_btree_exhaustive_validation.store(true);
1385+
SetCordBtreeExhaustiveValidation(true);
13861386
EXPECT_FALSE(CordRepBtree::IsValid(tree));
13871387
EXPECT_FALSE(CordRepBtree::IsValid(tree, true));
13881388
EXPECT_FALSE(CordRepBtree::IsValid(tree, false));

absl/strings/internal/cordz_handle.cc

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,60 @@
1616
#include <atomic>
1717

1818
#include "absl/base/internal/raw_logging.h" // For ABSL_RAW_CHECK
19-
#include "absl/base/internal/spinlock.h"
19+
#include "absl/synchronization/mutex.h"
2020

2121
namespace absl {
2222
ABSL_NAMESPACE_BEGIN
2323
namespace cord_internal {
2424

25-
using ::absl::base_internal::SpinLockHolder;
25+
namespace {
2626

27-
ABSL_CONST_INIT CordzHandle::Queue CordzHandle::global_queue_(absl::kConstInit);
27+
struct Queue {
28+
Queue() = default;
29+
30+
absl::Mutex mutex;
31+
std::atomic<CordzHandle*> dq_tail ABSL_GUARDED_BY(mutex){nullptr};
32+
33+
// Returns true if this delete queue is empty. This method does not acquire
34+
// the lock, but does a 'load acquire' observation on the delete queue tail.
35+
// It is used inside Delete() to check for the presence of a delete queue
36+
// without holding the lock. The assumption is that the caller is in the
37+
// state of 'being deleted', and can not be newly discovered by a concurrent
38+
// 'being constructed' snapshot instance. Practically, this means that any
39+
// such discovery (`find`, 'first' or 'next', etc) must have proper 'happens
40+
// before / after' semantics and atomic fences.
41+
bool IsEmpty() const ABSL_NO_THREAD_SAFETY_ANALYSIS {
42+
return dq_tail.load(std::memory_order_acquire) == nullptr;
43+
}
44+
};
45+
46+
static Queue* GlobalQueue() {
47+
static Queue* global_queue = new Queue;
48+
return global_queue;
49+
}
50+
51+
} // namespace
2852

2953
CordzHandle::CordzHandle(bool is_snapshot) : is_snapshot_(is_snapshot) {
54+
Queue* global_queue = GlobalQueue();
3055
if (is_snapshot) {
31-
SpinLockHolder lock(&queue_->mutex);
32-
CordzHandle* dq_tail = queue_->dq_tail.load(std::memory_order_acquire);
56+
MutexLock lock(&global_queue->mutex);
57+
CordzHandle* dq_tail =
58+
global_queue->dq_tail.load(std::memory_order_acquire);
3359
if (dq_tail != nullptr) {
3460
dq_prev_ = dq_tail;
3561
dq_tail->dq_next_ = this;
3662
}
37-
queue_->dq_tail.store(this, std::memory_order_release);
63+
global_queue->dq_tail.store(this, std::memory_order_release);
3864
}
3965
}
4066

4167
CordzHandle::~CordzHandle() {
42-
ODRCheck();
68+
Queue* global_queue = GlobalQueue();
4369
if (is_snapshot_) {
4470
std::vector<CordzHandle*> to_delete;
4571
{
46-
SpinLockHolder lock(&queue_->mutex);
72+
MutexLock lock(&global_queue->mutex);
4773
CordzHandle* next = dq_next_;
4874
if (dq_prev_ == nullptr) {
4975
// We were head of the queue, delete every CordzHandle until we reach
@@ -59,7 +85,7 @@ CordzHandle::~CordzHandle() {
5985
if (next) {
6086
next->dq_prev_ = dq_prev_;
6187
} else {
62-
queue_->dq_tail.store(dq_prev_, std::memory_order_release);
88+
global_queue->dq_tail.store(dq_prev_, std::memory_order_release);
6389
}
6490
}
6591
for (CordzHandle* handle : to_delete) {
@@ -69,16 +95,15 @@ CordzHandle::~CordzHandle() {
6995
}
7096

7197
bool CordzHandle::SafeToDelete() const {
72-
return is_snapshot_ || queue_->IsEmpty();
98+
return is_snapshot_ || GlobalQueue()->IsEmpty();
7399
}
74100

75101
void CordzHandle::Delete(CordzHandle* handle) {
76102
assert(handle);
77103
if (handle) {
78-
handle->ODRCheck();
79-
Queue* const queue = handle->queue_;
104+
Queue* const queue = GlobalQueue();
80105
if (!handle->SafeToDelete()) {
81-
SpinLockHolder lock(&queue->mutex);
106+
MutexLock lock(&queue->mutex);
82107
CordzHandle* dq_tail = queue->dq_tail.load(std::memory_order_acquire);
83108
if (dq_tail != nullptr) {
84109
handle->dq_prev_ = dq_tail;
@@ -93,8 +118,9 @@ void CordzHandle::Delete(CordzHandle* handle) {
93118

94119
std::vector<const CordzHandle*> CordzHandle::DiagnosticsGetDeleteQueue() {
95120
std::vector<const CordzHandle*> handles;
96-
SpinLockHolder lock(&global_queue_.mutex);
97-
CordzHandle* dq_tail = global_queue_.dq_tail.load(std::memory_order_acquire);
121+
Queue* global_queue = GlobalQueue();
122+
MutexLock lock(&global_queue->mutex);
123+
CordzHandle* dq_tail = global_queue->dq_tail.load(std::memory_order_acquire);
98124
for (const CordzHandle* p = dq_tail; p; p = p->dq_prev_) {
99125
handles.push_back(p);
100126
}
@@ -103,13 +129,13 @@ std::vector<const CordzHandle*> CordzHandle::DiagnosticsGetDeleteQueue() {
103129

104130
bool CordzHandle::DiagnosticsHandleIsSafeToInspect(
105131
const CordzHandle* handle) const {
106-
ODRCheck();
107132
if (!is_snapshot_) return false;
108133
if (handle == nullptr) return true;
109134
if (handle->is_snapshot_) return false;
110135
bool snapshot_found = false;
111-
SpinLockHolder lock(&queue_->mutex);
112-
for (const CordzHandle* p = queue_->dq_tail; p; p = p->dq_prev_) {
136+
Queue* global_queue = GlobalQueue();
137+
MutexLock lock(&global_queue->mutex);
138+
for (const CordzHandle* p = global_queue->dq_tail; p; p = p->dq_prev_) {
113139
if (p == handle) return !snapshot_found;
114140
if (p == this) snapshot_found = true;
115141
}
@@ -119,13 +145,13 @@ bool CordzHandle::DiagnosticsHandleIsSafeToInspect(
119145

120146
std::vector<const CordzHandle*>
121147
CordzHandle::DiagnosticsGetSafeToInspectDeletedHandles() {
122-
ODRCheck();
123148
std::vector<const CordzHandle*> handles;
124149
if (!is_snapshot()) {
125150
return handles;
126151
}
127152

128-
SpinLockHolder lock(&queue_->mutex);
153+
Queue* global_queue = GlobalQueue();
154+
MutexLock lock(&global_queue->mutex);
129155
for (const CordzHandle* p = dq_next_; p != nullptr; p = p->dq_next_) {
130156
if (!p->is_snapshot()) {
131157
handles.push_back(p);

absl/strings/internal/cordz_handle.h

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020

2121
#include "absl/base/config.h"
2222
#include "absl/base/internal/raw_logging.h"
23-
#include "absl/base/internal/spinlock.h"
24-
#include "absl/synchronization/mutex.h"
2523

2624
namespace absl {
2725
ABSL_NAMESPACE_BEGIN
@@ -79,37 +77,6 @@ class CordzHandle {
7977
virtual ~CordzHandle();
8078

8179
private:
82-
// Global queue data. CordzHandle stores a pointer to the global queue
83-
// instance to harden against ODR violations.
84-
struct Queue {
85-
constexpr explicit Queue(absl::ConstInitType)
86-
: mutex(absl::kConstInit,
87-
absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL) {}
88-
89-
absl::base_internal::SpinLock mutex;
90-
std::atomic<CordzHandle*> dq_tail ABSL_GUARDED_BY(mutex){nullptr};
91-
92-
// Returns true if this delete queue is empty. This method does not acquire
93-
// the lock, but does a 'load acquire' observation on the delete queue tail.
94-
// It is used inside Delete() to check for the presence of a delete queue
95-
// without holding the lock. The assumption is that the caller is in the
96-
// state of 'being deleted', and can not be newly discovered by a concurrent
97-
// 'being constructed' snapshot instance. Practically, this means that any
98-
// such discovery (`find`, 'first' or 'next', etc) must have proper 'happens
99-
// before / after' semantics and atomic fences.
100-
bool IsEmpty() const ABSL_NO_THREAD_SAFETY_ANALYSIS {
101-
return dq_tail.load(std::memory_order_acquire) == nullptr;
102-
}
103-
};
104-
105-
void ODRCheck() const {
106-
#ifndef NDEBUG
107-
ABSL_RAW_CHECK(queue_ == &global_queue_, "ODR violation in Cord");
108-
#endif
109-
}
110-
111-
ABSL_CONST_INIT static Queue global_queue_;
112-
Queue* const queue_ = &global_queue_;
11380
const bool is_snapshot_;
11481

11582
// dq_prev_ and dq_next_ require the global queue mutex to be held.

0 commit comments

Comments
 (0)