Skip to content

Commit ded90c7

Browse files
Fix lock in Immediate Control Board #5290 (#5289)
1 parent edea306 commit ded90c7

File tree

3 files changed

+34
-16
lines changed

3 files changed

+34
-16
lines changed

ydb/core/control/immediate_control_board_impl.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@
77
namespace NKikimr {
88

99
bool TControlBoard::RegisterLocalControl(TControlWrapper control, TString name) {
10-
bool result = true;
11-
if (Board.Has(name)) {
12-
result = false;
13-
}
14-
Board.Insert(name, control.Control);
15-
return result;
10+
TIntrusivePtr<TControl> ptr;
11+
bool result = Board.Swap(name, control.Control, ptr);
12+
return !result;
1613
}
1714

1815
bool TControlBoard::RegisterSharedControl(TControlWrapper& control, TString name) {
19-
auto& ptr = Board.InsertIfAbsent(name, control.Control);
16+
TIntrusivePtr<TControl> ptr;
17+
if (Board.Get(name, ptr)) {
18+
control.Control = ptr;
19+
return false;
20+
}
21+
ptr = Board.InsertIfAbsent(name, control.Control);
2022
if (control.Control == ptr) {
2123
return true;
2224
} else {

ydb/core/control/immediate_control_board_ut.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,17 @@ Y_UNIT_TEST_SUITE(ControlImplementationTests) {
111111

112112
Y_UNIT_TEST(TestParallelRegisterSharedControl) {
113113
void* (*parallelJob)(void*) = [](void *controlBoard) -> void *{
114-
TControlBoard *Icb = reinterpret_cast<TControlBoard *>(controlBoard);
115-
TControlWrapper control1(1, 1, 1);
116-
Icb->RegisterSharedControl(control1, "sharedControl");
117-
// Useless because running this test with --sanitize=thread cannot reveal
118-
// race condition in Icb->RegisterLocalControl(...) without mutex
119-
TControlWrapper control2(2, 2, 2);
120-
TControlWrapper control2_origin(control2);
121-
Icb->RegisterLocalControl(control2, "localControl");
122-
UNIT_ASSERT_EQUAL(control2, control2_origin);
114+
for (ui64 i = 0; i < 10000; ++i) {
115+
TControlBoard *Icb = reinterpret_cast<TControlBoard *>(controlBoard);
116+
TControlWrapper control1(1, 1, 1);
117+
Icb->RegisterSharedControl(control1, "sharedControl");
118+
// Useless because running this test with --sanitize=thread cannot reveal
119+
// race condition in Icb->RegisterLocalControl(...) without mutex
120+
TControlWrapper control2(2, 2, 2);
121+
TControlWrapper control2_origin(control2);
122+
Icb->RegisterLocalControl(control2, "localControl");
123+
UNIT_ASSERT_EQUAL(control2, control2_origin);
124+
}
123125
return nullptr;
124126
};
125127
TIntrusivePtr<TControlBoard> Icb(new TControlBoard);

ydb/core/util/concurrent_rw_hash.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,20 @@ class TConcurrentRWHashMap {
4949
bucket.Map[key] = value;
5050
}
5151

52+
bool Swap(const K& key, const V& value, V& out_prev_value) {
53+
TBucket& bucket = GetBucketForKey(key);
54+
TWriteGuard guard(bucket.RWLock);
55+
56+
typename TActualMap::iterator it = bucket.Map.find(key);
57+
if (it != bucket.Map.end()) {
58+
out_prev_value = it->second;
59+
it->second = value;
60+
return true;
61+
}
62+
bucket.Map.insert(std::make_pair(key, value));
63+
return false;
64+
}
65+
5266
V& InsertIfAbsent(const K& key, const V& value) {
5367
TBucket& bucket = GetBucketForKey(key);
5468
TWriteGuard guard(bucket.RWLock);

0 commit comments

Comments
 (0)