Skip to content

Commit bcc26d0

Browse files
[BugFix] Fix race condition in merge_isomorphic_profiles (backport StarRocks#59809) (StarRocks#59823)
Signed-off-by: stdpain <[email protected]> Co-authored-by: stdpain <[email protected]>
1 parent 14b24e7 commit bcc26d0

File tree

3 files changed

+76
-8
lines changed

3 files changed

+76
-8
lines changed

be/src/util/runtime_profile.cpp

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,16 @@ RuntimeProfile* RuntimeProfile::get_child(const std::string& name) {
360360
return it->second;
361361
}
362362

363+
RuntimeProfile* RuntimeProfile::get_child_unlock(const std::string& name) {
364+
auto it = _child_map.find(name);
365+
366+
if (it == _child_map.end()) {
367+
return nullptr;
368+
}
369+
370+
return it->second;
371+
}
372+
363373
RuntimeProfile* RuntimeProfile::get_child(const size_t index) {
364374
std::lock_guard<std::mutex> l(_children_lock);
365375
if (index >= _children.size()) {
@@ -857,6 +867,13 @@ RuntimeProfile::EventSequence* RuntimeProfile::add_event_sequence(const std::str
857867
_event_sequence_map[name] = timer;
858868
return timer;
859869
}
870+
template <class Visitor>
871+
void RuntimeProfile::foreach_children(Visitor&& callback) {
872+
std::lock_guard guard(_children_lock);
873+
for (auto& child : _children) {
874+
callback(child.first, child.second);
875+
}
876+
}
860877

861878
RuntimeProfile* RuntimeProfile::merge_isomorphic_profiles(ObjectPool* obj_pool, std::vector<RuntimeProfile*>& profiles,
862879
bool require_identical) {
@@ -1027,19 +1044,28 @@ RuntimeProfile* RuntimeProfile::merge_isomorphic_profiles(ObjectPool* obj_pool,
10271044
size_t max_child_size = 0;
10281045
RuntimeProfile* profile_with_full_child = nullptr;
10291046
for (auto* profile : profiles) {
1030-
if (profile->_children.size() > max_child_size) {
1047+
if (profile->num_children() > max_child_size) {
10311048
max_child_size = profile->_children.size();
10321049
profile_with_full_child = profile;
10331050
}
10341051
}
10351052
if (profile_with_full_child != nullptr) {
10361053
bool identical = true;
1037-
for (size_t i = 0; i < max_child_size; i++) {
1038-
auto& prototype_kv = profile_with_full_child->_children[i];
1039-
const std::string& child_name = prototype_kv.first->name();
1054+
1055+
profile_with_full_child->foreach_children([&obj_pool, &profiles, &identical, require_identical,
1056+
merged_profile,
1057+
profile_with_full_child](RuntimeProfile* child, bool indent) {
1058+
const std::string& child_name = child->name();
10401059
std::vector<RuntimeProfile*> sub_profiles;
10411060
for (auto* profile : profiles) {
1042-
auto* child = profile->get_child(child_name);
1061+
RuntimeProfile* child = nullptr;
1062+
// We've already acquired the profile's lock, don't acquire again
1063+
if (profile == profile_with_full_child) {
1064+
child = profile->get_child_unlock(child_name);
1065+
} else {
1066+
child = profile->get_child(child_name);
1067+
}
1068+
10431069
if (child == nullptr) {
10441070
identical = false;
10451071
if (require_identical) {
@@ -1051,8 +1077,9 @@ RuntimeProfile* RuntimeProfile::merge_isomorphic_profiles(ObjectPool* obj_pool,
10511077
sub_profiles.push_back(child);
10521078
}
10531079
auto* merged_child = merge_isomorphic_profiles(obj_pool, sub_profiles, require_identical);
1054-
merged_profile->add_child(merged_child, prototype_kv.second, nullptr);
1055-
}
1080+
merged_profile->add_child(merged_child, indent, nullptr);
1081+
});
1082+
10561083
if (require_identical && !identical) {
10571084
merged_profile->add_info_string("NotIdentical");
10581085
}

be/src/util/runtime_profile.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <atomic>
4141
#include <functional>
4242
#include <iostream>
43+
#include <mutex>
4344
#include <optional>
4445
#include <thread>
4546
#include <unordered_set>
@@ -438,6 +439,9 @@ class RuntimeProfile {
438439
return add_child_counter(name, type, strategy, ROOT_COUNTER);
439440
}
440441

442+
template <class Visitor>
443+
void foreach_children(Visitor&& callback);
444+
441445
// Add a derived counter with 'name'/'type'. The counter is owned by the
442446
// RuntimeProfile object.
443447
// If parent_name is a non-empty string, the counter is added as a child of
@@ -506,7 +510,10 @@ class RuntimeProfile {
506510
// Divides all counters by n
507511
void divide(int n);
508512

509-
size_t num_children() const { return _child_map.size(); }
513+
size_t num_children() const {
514+
std::lock_guard guard(_children_lock);
515+
return _child_map.size();
516+
}
510517

511518
// Get child of given name
512519
RuntimeProfile* get_child(const std::string& name);
@@ -579,6 +586,8 @@ class RuntimeProfile {
579586
Counter* add_counter_unlock(const std::string& name, TUnit::type type, const TCounterStrategy& strategy,
580587
const std::string& parent_name);
581588

589+
RuntimeProfile* get_child_unlock(const std::string& name);
590+
582591
RuntimeProfile* _parent;
583592

584593
// Pool for allocated counters. Usually owned by the creator of this

be/test/util/runtime_profile_test.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@
1919

2020
#include <gtest/gtest.h>
2121

22+
#include <thread>
23+
#include <tuple>
24+
2225
#include "common/logging.h"
26+
#include "common/object_pool.h"
27+
#include "fmt/format.h"
2328
#include "gen_cpp/RuntimeProfile_types.h"
2429

2530
namespace starrocks {
@@ -533,4 +538,31 @@ TEST(TestRuntimeProfile, testUpdateWithOldAndNewProfile) {
533538
ASSERT_EQ(2, child_profile->get_version());
534539
}
535540

541+
TEST(TestRuntimeProfile, testRaceMergeProfiles) {
542+
ObjectPool pool;
543+
std::vector<RuntimeProfile*> profiles;
544+
const size_t init_profile_size = 10;
545+
for (size_t i = 0; i < init_profile_size; ++i) {
546+
profiles.push_back(pool.add(new RuntimeProfile("test-name")));
547+
}
548+
549+
const size_t loop_test_size = 10;
550+
551+
// race between merge_isomorphic_profiles and create_child
552+
std::thread thr1([&profiles]() {
553+
for (size_t i = 0; i < loop_test_size; ++i) {
554+
ObjectPool pool;
555+
std::ignore = RuntimeProfile::merge_isomorphic_profiles(&pool, profiles);
556+
}
557+
});
558+
std::thread thr2([&profiles]() {
559+
for (size_t i = 0; i < loop_test_size; ++i) {
560+
profiles[0]->create_child(fmt::format("MERGE{}", i), true, true);
561+
}
562+
});
563+
564+
thr1.join();
565+
thr2.join();
566+
}
567+
536568
} // namespace starrocks

0 commit comments

Comments
 (0)