Skip to content
This repository was archived by the owner on Jan 31, 2025. It is now read-only.

Commit 3c1b74f

Browse files
authored
Use absl::Mutex in TrackManager (#4685)
* Use absl::Mutex in TrackManager TrackManager is a class that is not as easy as it should be to navigate. The main complexity lies in the fact that there are two threads that are accessing the class. The capture thread is inserting timers and therefore creating the tracks when needed and the main thread is reading the tracks to sort then and render the visible ones (a detailed picture can be found in http://go/stadia-orbit-track-creation#heading=h.vpgt3chmpmpb). We had issues in the class because of multi-threading (and currently it seems Daniel found a new one), so in this PR I'm cleaning this class by replacing the recursive_mutex with an absl::Mutex. I don't see how this could solve the crash found by Daniel but at least the class might be easier to understand when looking for the issue. We were using a recursive_mutex because some public methods were calling another ones where both of then needs the mutex to be locked. But as we don't have recursion, this can be solved by splitting some methods. In this PR we are: - Guarding all the containers that needs to be locked (ABSL_GUARDED_BY) - Locking all the public methods that are accessing the containers. - Not locking the private methods, as they are only called by the class itself. - Manually assuring that the locked methods don't call another public method that lock the mutex. I'm not sure about if the last point is the best thing to do but I feel that this is an improvement, do you have another suggestion? Test: Load a capture with all kind of tracks. Also take a capture. * Fixup: Reader Lock and Shared_Locks_Required * Fixup: WriterMutexLock
1 parent bdff513 commit 3c1b74f

File tree

2 files changed

+71
-43
lines changed

2 files changed

+71
-43
lines changed

src/OrbitGl/TrackManager.cpp

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@ TrackManager::TrackManager(TrackContainer* track_container, TimelineInfoInterfac
6161
}
6262

6363
std::vector<Track*> TrackManager::GetAllTracks() const {
64-
std::lock_guard<std::recursive_mutex> lock(mutex_);
64+
absl::ReaderMutexLock lock(&mutex_);
65+
return GetAllTracksInternal();
66+
}
67+
68+
std::vector<Track*> TrackManager::GetAllTracksInternal() const {
6569
std::vector<Track*> tracks;
6670
for (const auto& track : all_tracks_) {
6771
tracks.push_back(track.get());
@@ -74,7 +78,7 @@ std::vector<Track*> TrackManager::GetAllTracks() const {
7478
}
7579

7680
std::vector<FrameTrack*> TrackManager::GetFrameTracks() const {
77-
std::lock_guard<std::recursive_mutex> lock(mutex_);
81+
absl::ReaderMutexLock lock(&mutex_);
7882
std::vector<FrameTrack*> tracks;
7983
for (const auto& [unused_id, track] : frame_tracks_) {
8084
tracks.push_back(track.get());
@@ -83,7 +87,7 @@ std::vector<FrameTrack*> TrackManager::GetFrameTracks() const {
8387
}
8488

8589
void TrackManager::SortTracks() {
86-
std::lock_guard<std::recursive_mutex> lock(mutex_);
90+
absl::WriterMutexLock lock(&mutex_);
8791
// Gather all tracks regardless of the process in sorted order
8892
std::vector<Track*> all_processes_sorted_tracks;
8993

@@ -124,7 +128,7 @@ void TrackManager::SortTracks() {
124128
if (absl::GetFlag(FLAGS_enable_tracepoint_feature)) {
125129
if (app_ != nullptr && app_->HasCaptureData()) {
126130
ThreadTrack* tracepoints_track =
127-
GetOrCreateThreadTrack(orbit_base::kAllThreadsOfAllProcessesTid);
131+
GetOrCreateThreadTrackInternal(orbit_base::kAllThreadsOfAllProcessesTid);
128132
if (!tracepoints_track->IsEmpty()) {
129133
all_processes_sorted_tracks.push_back(tracepoints_track);
130134
}
@@ -133,7 +137,7 @@ void TrackManager::SortTracks() {
133137

134138
// Process track.
135139
if (app_ != nullptr && app_->HasCaptureData()) {
136-
ThreadTrack* process_track = GetOrCreateThreadTrack(orbit_base::kAllProcessThreadsTid);
140+
ThreadTrack* process_track = GetOrCreateThreadTrackInternal(orbit_base::kAllProcessThreadsTid);
137141
if (!process_track->IsEmpty()) {
138142
all_processes_sorted_tracks.push_back(process_track);
139143
}
@@ -360,7 +364,7 @@ void TrackManager::AddFrameTrack(const std::shared_ptr<FrameTrack>& frame_track)
360364
}
361365

362366
void TrackManager::RemoveFrameTrack(uint64_t function_id) {
363-
std::lock_guard<std::recursive_mutex> lock(mutex_);
367+
absl::WriterMutexLock lock(&mutex_);
364368
deleted_tracks_.push_back(frame_tracks_[function_id]);
365369
frame_tracks_.erase(function_id);
366370

@@ -439,7 +443,7 @@ Track* TrackManager::GetOrCreateTrackFromTimerInfo(const TimerInfo& timer_info)
439443
}
440444

441445
SchedulerTrack* TrackManager::GetOrCreateSchedulerTrack() {
442-
std::lock_guard<std::recursive_mutex> lock(mutex_);
446+
absl::WriterMutexLock lock(&mutex_);
443447
if (scheduler_track_ == nullptr) {
444448
auto [unused, timer_data] = capture_data_->CreateTimerData();
445449
scheduler_track_ =
@@ -451,7 +455,11 @@ SchedulerTrack* TrackManager::GetOrCreateSchedulerTrack() {
451455
}
452456

453457
ThreadTrack* TrackManager::GetOrCreateThreadTrack(uint32_t tid) {
454-
std::lock_guard<std::recursive_mutex> lock(mutex_);
458+
absl::WriterMutexLock lock(&mutex_);
459+
return GetOrCreateThreadTrackInternal(tid);
460+
}
461+
462+
ThreadTrack* TrackManager::GetOrCreateThreadTrackInternal(uint32_t tid) {
455463
std::shared_ptr<ThreadTrack> track = thread_tracks_[tid];
456464
if (track == nullptr) {
457465
auto* thread_track_data_provider = capture_data_->GetThreadTrackDataProvider();
@@ -466,7 +474,7 @@ ThreadTrack* TrackManager::GetOrCreateThreadTrack(uint32_t tid) {
466474
}
467475

468476
std::optional<ThreadTrack*> TrackManager::GetThreadTrack(uint32_t tid) const {
469-
std::lock_guard<std::recursive_mutex> lock(mutex_);
477+
absl::ReaderMutexLock lock(&mutex_);
470478
if (!thread_tracks_.contains(tid)) {
471479
return std::nullopt;
472480
}
@@ -475,7 +483,7 @@ std::optional<ThreadTrack*> TrackManager::GetThreadTrack(uint32_t tid) const {
475483
}
476484

477485
GpuTrack* TrackManager::GetOrCreateGpuTrack(uint64_t timeline_hash) {
478-
std::lock_guard<std::recursive_mutex> lock(mutex_);
486+
absl::WriterMutexLock lock(&mutex_);
479487
std::string timeline =
480488
app_->GetStringManager()->Get(timeline_hash).value_or(std::to_string(timeline_hash));
481489
std::shared_ptr<GpuTrack> track = gpu_tracks_[timeline];
@@ -492,7 +500,7 @@ GpuTrack* TrackManager::GetOrCreateGpuTrack(uint64_t timeline_hash) {
492500
}
493501

494502
VariableTrack* TrackManager::GetOrCreateVariableTrack(std::string_view name) {
495-
std::lock_guard<std::recursive_mutex> lock(mutex_);
503+
absl::WriterMutexLock lock(&mutex_);
496504

497505
auto existing_track = variable_tracks_.find(name);
498506
if (existing_track != variable_tracks_.end()) return existing_track->second.get();
@@ -505,7 +513,7 @@ VariableTrack* TrackManager::GetOrCreateVariableTrack(std::string_view name) {
505513
}
506514

507515
AsyncTrack* TrackManager::GetOrCreateAsyncTrack(std::string_view name) {
508-
std::lock_guard<std::recursive_mutex> lock(mutex_);
516+
absl::WriterMutexLock lock(&mutex_);
509517

510518
auto existing_track = async_tracks_.find(name);
511519
if (existing_track != async_tracks_.end()) return existing_track->second.get();
@@ -520,7 +528,7 @@ AsyncTrack* TrackManager::GetOrCreateAsyncTrack(std::string_view name) {
520528
}
521529

522530
FrameTrack* TrackManager::GetOrCreateFrameTrack(uint64_t function_id) {
523-
std::lock_guard<std::recursive_mutex> lock(mutex_);
531+
absl::WriterMutexLock lock(&mutex_);
524532
if (auto track_it = frame_tracks_.find(function_id); track_it != frame_tracks_.end()) {
525533
return track_it->second.get();
526534
}
@@ -541,49 +549,64 @@ FrameTrack* TrackManager::GetOrCreateFrameTrack(uint64_t function_id) {
541549
return track.get();
542550
}
543551

552+
SystemMemoryTrack* TrackManager::GetSystemMemoryTrack() const {
553+
absl::ReaderMutexLock lock(&mutex_);
554+
return system_memory_track_.get();
555+
}
556+
557+
CGroupAndProcessMemoryTrack* TrackManager::GetCGroupAndProcessMemoryTrack() const {
558+
absl::ReaderMutexLock lock(&mutex_);
559+
return cgroup_and_process_memory_track_.get();
560+
}
561+
562+
PageFaultsTrack* TrackManager::GetPageFaultsTrack() const {
563+
absl::ReaderMutexLock lock(&mutex_);
564+
return page_faults_track_.get();
565+
}
566+
544567
SystemMemoryTrack* TrackManager::CreateAndGetSystemMemoryTrack() {
545-
std::lock_guard<std::recursive_mutex> lock(mutex_);
568+
absl::WriterMutexLock lock(&mutex_);
546569
if (system_memory_track_ == nullptr) {
547570
system_memory_track_ = std::make_shared<SystemMemoryTrack>(
548571
track_container_, timeline_info_, viewport_, layout_, module_manager_, capture_data_);
549572
AddTrack(system_memory_track_);
550573
}
551574

552-
return GetSystemMemoryTrack();
575+
return system_memory_track_.get();
553576
}
554577

555578
CGroupAndProcessMemoryTrack* TrackManager::CreateAndGetCGroupAndProcessMemoryTrack(
556579
std::string_view cgroup_name) {
557-
std::lock_guard<std::recursive_mutex> lock(mutex_);
580+
absl::WriterMutexLock lock(&mutex_);
558581
if (cgroup_and_process_memory_track_ == nullptr) {
559582
cgroup_and_process_memory_track_ = std::make_shared<CGroupAndProcessMemoryTrack>(
560583
track_container_, timeline_info_, viewport_, layout_, std::string{cgroup_name},
561584
module_manager_, capture_data_);
562585
AddTrack(cgroup_and_process_memory_track_);
563586
}
564587

565-
return GetCGroupAndProcessMemoryTrack();
588+
return cgroup_and_process_memory_track_.get();
566589
}
567590

568591
PageFaultsTrack* TrackManager::CreateAndGetPageFaultsTrack(std::string_view cgroup_name,
569592
uint64_t memory_sampling_period_ms) {
570-
std::lock_guard<std::recursive_mutex> lock(mutex_);
593+
absl::WriterMutexLock lock(&mutex_);
571594
if (page_faults_track_ == nullptr) {
572595
page_faults_track_ = std::make_shared<PageFaultsTrack>(
573596
track_container_, timeline_info_, viewport_, layout_, std::string{cgroup_name},
574597
memory_sampling_period_ms, module_manager_, capture_data_);
575598
AddTrack(page_faults_track_);
576599
}
577-
return GetPageFaultsTrack();
600+
return page_faults_track_.get();
578601
}
579602

580603
// TODO(b/177200020): Move to TrackContainer after assuring to have only one thread in the UI.
581604
std::pair<uint64_t, uint64_t> TrackManager::GetTracksMinMaxTimestamps() const {
582605
uint64_t min_time = std::numeric_limits<uint64_t>::max();
583606
uint64_t max_time = std::numeric_limits<uint64_t>::min();
584607

585-
std::lock_guard<std::recursive_mutex> lock(mutex_);
586-
for (auto& track : GetAllTracks()) {
608+
absl::ReaderMutexLock lock(&mutex_);
609+
for (auto& track : GetAllTracksInternal()) {
587610
if (!track->IsEmpty()) {
588611
min_time = std::min(min_time, track->GetMinTime());
589612
max_time = std::max(max_time, track->GetMaxTime());

src/OrbitGl/include/OrbitGl/TrackManager.h

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,12 @@ class TrackManager {
7676
VariableTrack* GetOrCreateVariableTrack(std::string_view name);
7777
AsyncTrack* GetOrCreateAsyncTrack(std::string_view name);
7878
FrameTrack* GetOrCreateFrameTrack(uint64_t function_id);
79-
[[nodiscard]] SystemMemoryTrack* GetSystemMemoryTrack() const {
80-
return system_memory_track_.get();
81-
}
79+
[[nodiscard]] SystemMemoryTrack* GetSystemMemoryTrack() const;
8280
[[nodiscard]] SystemMemoryTrack* CreateAndGetSystemMemoryTrack();
83-
[[nodiscard]] CGroupAndProcessMemoryTrack* GetCGroupAndProcessMemoryTrack() const {
84-
return cgroup_and_process_memory_track_.get();
85-
}
81+
[[nodiscard]] CGroupAndProcessMemoryTrack* GetCGroupAndProcessMemoryTrack() const;
8682
[[nodiscard]] CGroupAndProcessMemoryTrack* CreateAndGetCGroupAndProcessMemoryTrack(
8783
std::string_view cgroup_name);
88-
PageFaultsTrack* GetPageFaultsTrack() const { return page_faults_track_.get(); }
84+
PageFaultsTrack* GetPageFaultsTrack() const;
8985
PageFaultsTrack* CreateAndGetPageFaultsTrack(std::string_view cgroup_name,
9086
uint64_t memory_sampling_period_ms);
9187

@@ -103,31 +99,40 @@ class TrackManager {
10399
[[nodiscard]] int FindMovingTrackIndex();
104100
void UpdateMovingTrackPositionInVisibleTracks();
105101
void SortTracks();
106-
[[nodiscard]] std::vector<ThreadTrack*> GetSortedThreadTracks();
102+
[[nodiscard]] std::vector<ThreadTrack*> GetSortedThreadTracks()
103+
ABSL_SHARED_LOCKS_REQUIRED(mutex_);
104+
[[nodiscard]] std::vector<Track*> GetAllTracksInternal() const ABSL_SHARED_LOCKS_REQUIRED(mutex_);
105+
ThreadTrack* GetOrCreateThreadTrackInternal(uint32_t tid) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
107106
// Filter tracks that are already sorted in sorted_tracks_.
108107
void UpdateVisibleTrackList();
109108
void DeletePendingTracks();
110109

111-
void AddTrack(const std::shared_ptr<Track>& track);
110+
void AddTrack(const std::shared_ptr<Track>& track) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
112111
void AddFrameTrack(const std::shared_ptr<FrameTrack>& frame_track);
113112

114-
// TODO(b/174655559): Use absl's mutex here.
115-
mutable std::recursive_mutex mutex_;
116-
117-
std::vector<std::shared_ptr<Track>> all_tracks_;
118-
absl::flat_hash_map<uint32_t, std::shared_ptr<ThreadTrack>> thread_tracks_;
119-
std::map<std::string, std::shared_ptr<AsyncTrack>, std::less<>> async_tracks_;
120-
std::map<std::string, std::shared_ptr<VariableTrack>, std::less<>> variable_tracks_;
113+
// This mutex is needed because two different threads will access the following containers. The
114+
// capture thread will insert tracks and the main thread will read the tracks from the containers
115+
// to generate the sorted list of visible tracks.
116+
mutable absl::Mutex mutex_;
117+
118+
std::vector<std::shared_ptr<Track>> all_tracks_ ABSL_GUARDED_BY(mutex_);
119+
absl::flat_hash_map<uint32_t, std::shared_ptr<ThreadTrack>> thread_tracks_
120+
ABSL_GUARDED_BY(mutex_);
121+
std::map<std::string, std::shared_ptr<AsyncTrack>, std::less<>> async_tracks_
122+
ABSL_GUARDED_BY(mutex_);
123+
std::map<std::string, std::shared_ptr<VariableTrack>, std::less<>> variable_tracks_
124+
ABSL_GUARDED_BY(mutex_);
121125
// Mapping from timeline to GPU tracks. Timeline name is used for stable ordering. In particular
122126
// we want the marker tracks next to their queue track. E.g. "gfx" and "gfx_markers" should appear
123127
// next to each other.
124-
std::map<std::string, std::shared_ptr<GpuTrack>> gpu_tracks_;
128+
std::map<std::string, std::shared_ptr<GpuTrack>> gpu_tracks_ ABSL_GUARDED_BY(mutex_);
125129
// Mapping from function id to frame tracks.
126-
std::map<uint64_t, std::shared_ptr<FrameTrack>> frame_tracks_;
127-
std::shared_ptr<SchedulerTrack> scheduler_track_;
128-
std::shared_ptr<SystemMemoryTrack> system_memory_track_;
129-
std::shared_ptr<CGroupAndProcessMemoryTrack> cgroup_and_process_memory_track_;
130-
std::shared_ptr<PageFaultsTrack> page_faults_track_;
130+
std::map<uint64_t, std::shared_ptr<FrameTrack>> frame_tracks_ ABSL_GUARDED_BY(mutex_);
131+
std::shared_ptr<SchedulerTrack> scheduler_track_ ABSL_GUARDED_BY(mutex_);
132+
std::shared_ptr<SystemMemoryTrack> system_memory_track_ ABSL_GUARDED_BY(mutex_);
133+
std::shared_ptr<CGroupAndProcessMemoryTrack> cgroup_and_process_memory_track_
134+
ABSL_GUARDED_BY(mutex_);
135+
std::shared_ptr<PageFaultsTrack> page_faults_track_ ABSL_GUARDED_BY(mutex_);
131136

132137
// This intermediatly stores tracks that have been deleted from one of the track vectors above
133138
// so they can safely be removed from the list of sorted and visible tracks.

0 commit comments

Comments
 (0)