Make SckPickerProxy thread-safe with a Mutex
Single-threaded access doesn't work because SckPickerProxy is a
singleton and clients typically use different capturers for enumeration
and capture, and runs them for those use cases on different threads.
Bug: webrtc:367915807
Change-Id: I8695ea38a21bf3a307dfd21d169ef5ca2fe43809
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/392780
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
Reviewed-by: Tomas Gunnarsson <tommi@webrtc.org>
Commit-Queue: Andreas Pehrson <apehrson@mozilla.com>
Cr-Commit-Position: refs/heads/main@{#44722}
diff --git a/modules/desktop_capture/mac/sck_picker_handle.mm b/modules/desktop_capture/mac/sck_picker_handle.mm
index c1b4db1..dd5391f 100644
--- a/modules/desktop_capture/mac/sck_picker_handle.mm
+++ b/modules/desktop_capture/mac/sck_picker_handle.mm
@@ -29,10 +29,7 @@
return g_picker;
}
- SckPickerProxy() : thread_checker_(SequenceChecker::kDetached) {}
-
- bool AtCapacity() const {
- RTC_DCHECK_RUN_ON(&thread_checker_);
+ bool AtCapacityLocked() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
return handle_count_ == kMaximumStreamCount;
}
@@ -42,8 +39,8 @@
ABSL_MUST_USE_RESULT std::optional<DesktopCapturer::SourceId>
AcquireSourceId() {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- if (AtCapacity()) {
+ MutexLock lock(&mutex_);
+ if (AtCapacityLocked()) {
return std::nullopt;
}
if (handle_count_ == 0) {
@@ -58,25 +55,26 @@
}
void RelinquishSourceId(DesktopCapturer::SourceId source) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
+ MutexLock lock(&mutex_);
handle_count_ -= 1;
if (handle_count_ > 0) {
return;
}
- // Detach now in case the next user (possibly after a long time) uses a
- // different thread.
- thread_checker_.Detach();
GetPicker().active = NO;
}
private:
- webrtc::SequenceChecker thread_checker_;
+ // SckPickerProxy is a process-wide singleton. ScreenCapturerSck and
+ // SckPickerHandle are largely single-threaded but may be used on different
+ // threads. For instance some clients use a capturer on one thread for
+ // enumeration and on another for frame capture. Since all those capturers
+ // share the same SckPickerProxy instance, it must be thread-safe.
+ Mutex mutex_;
// 100 is an arbitrary number that seems high enough to never get reached,
// while still providing a reasonably low upper bound.
static constexpr size_t kMaximumStreamCount = 100;
- size_t handle_count_ RTC_GUARDED_BY(thread_checker_) = 0;
- DesktopCapturer::SourceId unique_source_id_ RTC_GUARDED_BY(thread_checker_) =
- 0;
+ size_t handle_count_ RTC_GUARDED_BY(mutex_) = 0;
+ DesktopCapturer::SourceId unique_source_id_ RTC_GUARDED_BY(mutex_) = 0;
};
class API_AVAILABLE(macos(14.0)) SckPickerHandle
@@ -90,7 +88,10 @@
return std::unique_ptr<SckPickerHandle>(new SckPickerHandle(proxy, *id));
}
- ~SckPickerHandle() { proxy_->RelinquishSourceId(source_); }
+ ~SckPickerHandle() {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ proxy_->RelinquishSourceId(source_);
+ }
SCContentSharingPicker* GetPicker() const override {
return proxy_->GetPicker();
@@ -100,8 +101,11 @@
private:
SckPickerHandle(SckPickerProxy* proxy, DesktopCapturer::SourceId source)
- : proxy_(proxy), source_(source) {}
+ : proxy_(proxy), source_(source) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ }
+ webrtc::SequenceChecker thread_checker_;
SckPickerProxy* const proxy_;
const DesktopCapturer::SourceId source_;
};
diff --git a/modules/desktop_capture/mac/screen_capturer_sck.mm b/modules/desktop_capture/mac/screen_capturer_sck.mm
index d6e3c2e..49c923e 100644
--- a/modules/desktop_capture/mac/screen_capturer_sck.mm
+++ b/modules/desktop_capture/mac/screen_capturer_sck.mm
@@ -72,6 +72,8 @@
void CaptureFrame() override;
bool GetSourceList(SourceList* sources) override;
bool SelectSource(SourceId id) override;
+ // Creates the SckPickerHandle if needed and not already done.
+ void EnsurePickerHandle();
// Prep for implementing DelegatedSourceListController interface, for now used
// by Start(). Triggers SCContentSharingPicker. Runs on the caller's thread.
void EnsureVisible();
@@ -218,15 +220,6 @@
: api_checker_(SequenceChecker::kDetached),
capture_options_(options),
picker_modes_(modes) {
- if (capture_options_.allow_sck_system_picker()) {
- picker_handle_ = CreateSckPickerHandle();
- }
- RTC_LOG(LS_INFO) << "ScreenCapturerSck " << this
- << " created. allow_sck_system_picker="
- << capture_options_.allow_sck_system_picker() << ", source="
- << (picker_handle_ ? picker_handle_->Source() : -1)
- << ", modes="
- << StringifiableSCContentSharingPickerMode{.modes_ = modes};
helper_ = [[SckHelper alloc] initWithCapturer:this];
}
@@ -322,9 +315,25 @@
}
}
+void ScreenCapturerSck::EnsurePickerHandle() {
+ RTC_DCHECK_RUN_ON(&api_checker_);
+ if (!picker_handle_ && capture_options_.allow_sck_system_picker()) {
+ picker_handle_ = CreateSckPickerHandle();
+ RTC_LOG(LS_INFO) << "ScreenCapturerSck " << this
+ << " Created picker handle. allow_sck_system_picker="
+ << capture_options_.allow_sck_system_picker()
+ << ", source="
+ << (picker_handle_ ? picker_handle_->Source() : -1)
+ << ", modes="
+ << StringifiableSCContentSharingPickerMode{
+ .modes_ = picker_modes_};
+ }
+}
+
void ScreenCapturerSck::EnsureVisible() {
RTC_DCHECK_RUN_ON(&api_checker_);
RTC_LOG(LS_INFO) << "ScreenCapturerSck " << this << " " << __func__ << ".";
+ EnsurePickerHandle();
if (picker_handle_) {
if (!picker_handle_registered_) {
picker_handle_registered_ = true;
@@ -433,15 +442,14 @@
bool ScreenCapturerSck::GetSourceList(SourceList* sources) {
RTC_DCHECK_RUN_ON(&api_checker_);
sources->clear();
- if (capture_options_.allow_sck_system_picker() && picker_handle_) {
+ EnsurePickerHandle();
+ if (picker_handle_) {
sources->push_back({picker_handle_->Source(), std::string()});
}
return true;
}
bool ScreenCapturerSck::SelectSource(SourceId id) {
- RTC_DCHECK_RUN_ON(&api_checker_);
-
if (capture_options_.allow_sck_system_picker()) {
return true;
}