Share ScreenDrawerLockPosix implementation
This change renames ScreenDrawerLockLinux into ScreenDrawerLockPosix and shares
it with Mac OSX.
Bug: webrtc:7950
Change-Id: Ib141781d2c35bfda0d6f9458fff235adbb643280
Reviewed-on: https://chromium-review.googlesource.com/607688
Commit-Queue: Zijie He <zijiehe@chromium.org>
Reviewed-by: Jamie Walch <jamiewalch@chromium.org>
Cr-Commit-Position: refs/heads/master@{#19393}diff --git a/webrtc/modules/desktop_capture/BUILD.gn b/webrtc/modules/desktop_capture/BUILD.gn
index caf27a3..92d670c 100644
--- a/webrtc/modules/desktop_capture/BUILD.gn
+++ b/webrtc/modules/desktop_capture/BUILD.gn
@@ -127,6 +127,8 @@
"screen_drawer.cc",
"screen_drawer.h",
"screen_drawer_linux.cc",
+ "screen_drawer_lock_posix.cc",
+ "screen_drawer_lock_posix.h",
"screen_drawer_mac.cc",
"screen_drawer_win.cc",
]
diff --git a/webrtc/modules/desktop_capture/screen_drawer_linux.cc b/webrtc/modules/desktop_capture/screen_drawer_linux.cc
index 9a9c20d..fa2cfa4 100644
--- a/webrtc/modules/desktop_capture/screen_drawer_linux.cc
+++ b/webrtc/modules/desktop_capture/screen_drawer_linux.cc
@@ -8,47 +8,22 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <semaphore.h>
#include <string.h>
#include <X11/Xlib.h>
#include <memory>
#include "webrtc/modules/desktop_capture/screen_drawer.h"
+#include "webrtc/modules/desktop_capture/screen_drawer_lock_posix.h"
#include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
#include "webrtc/rtc_base/checks.h"
+#include "webrtc/rtc_base/ptr_util.h"
#include "webrtc/system_wrappers/include/sleep.h"
namespace webrtc {
namespace {
-static constexpr char kSemaphoreName[] =
- "/global-screen-drawer-linux-54fe5552-8047-11e6-a725-3f429a5b4fb4";
-
-class ScreenDrawerLockLinux : public ScreenDrawerLock {
- public:
- ScreenDrawerLockLinux();
- ~ScreenDrawerLockLinux() override;
-
- private:
- sem_t* semaphore_;
-};
-
-ScreenDrawerLockLinux::ScreenDrawerLockLinux() {
- semaphore_ =
- sem_open(kSemaphoreName, O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO, 1);
- sem_wait(semaphore_);
-}
-
-ScreenDrawerLockLinux::~ScreenDrawerLockLinux() {
- sem_post(semaphore_);
- sem_close(semaphore_);
- // sem_unlink(kSemaphoreName);
-}
-
// A ScreenDrawer implementation for X11.
class ScreenDrawerLinux : public ScreenDrawer {
public:
@@ -187,13 +162,13 @@
// static
std::unique_ptr<ScreenDrawerLock> ScreenDrawerLock::Create() {
- return std::unique_ptr<ScreenDrawerLock>(new ScreenDrawerLockLinux());
+ return rtc::MakeUnique<ScreenDrawerLockPosix>();
}
// static
std::unique_ptr<ScreenDrawer> ScreenDrawer::Create() {
if (SharedXDisplay::CreateDefault().get()) {
- return std::unique_ptr<ScreenDrawer>(new ScreenDrawerLinux());
+ return rtc::MakeUnique<ScreenDrawerLinux>();
}
return nullptr;
}
diff --git a/webrtc/modules/desktop_capture/screen_drawer_lock_posix.cc b/webrtc/modules/desktop_capture/screen_drawer_lock_posix.cc
new file mode 100644
index 0000000..5b6bc9c
--- /dev/null
+++ b/webrtc/modules/desktop_capture/screen_drawer_lock_posix.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/desktop_capture/screen_drawer_lock_posix.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "webrtc/rtc_base/checks.h"
+#include "webrtc/rtc_base/logging.h"
+
+namespace webrtc {
+
+namespace {
+
+// A uuid as the name of semaphore.
+static constexpr char kSemaphoreName[] = "GSDL54fe5552804711e6a7253f429a";
+
+} // namespace
+
+ScreenDrawerLockPosix::ScreenDrawerLockPosix()
+ : ScreenDrawerLockPosix(kSemaphoreName) {}
+
+ScreenDrawerLockPosix::ScreenDrawerLockPosix(const char* name) {
+ semaphore_ = sem_open(name, O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO, 1);
+ if (semaphore_ == SEM_FAILED) {
+ LOG_ERRNO(LS_ERROR) << "Failed to create named semaphore with " << name;
+ RTC_NOTREACHED();
+ }
+
+ sem_wait(semaphore_);
+}
+
+ScreenDrawerLockPosix::~ScreenDrawerLockPosix() {
+ if (semaphore_ == SEM_FAILED) {
+ return;
+ }
+
+ sem_post(semaphore_);
+ sem_close(semaphore_);
+ // sem_unlink a named semaphore won't wait until other clients to release the
+ // sem_t. So if a new process starts, it will sem_open a different kernel
+ // object with the same name and eventually breaks the cross-process lock.
+}
+
+// static
+void ScreenDrawerLockPosix::Unlink(const char* name) {
+ sem_unlink(name);
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/screen_drawer_lock_posix.h b/webrtc/modules/desktop_capture/screen_drawer_lock_posix.h
new file mode 100644
index 0000000..a768066
--- /dev/null
+++ b/webrtc/modules/desktop_capture/screen_drawer_lock_posix.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_SCREEN_DRAWER_LOCK_POSIX_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_SCREEN_DRAWER_LOCK_POSIX_H_
+
+#include <semaphore.h>
+
+#include "webrtc/modules/desktop_capture/screen_drawer.h"
+
+namespace webrtc {
+
+class ScreenDrawerLockPosix final : public ScreenDrawerLock {
+ public:
+ ScreenDrawerLockPosix();
+ // Provides a name other than the default one for test only.
+ explicit ScreenDrawerLockPosix(const char* name);
+ ~ScreenDrawerLockPosix() override;
+
+ // Unlinks the named semaphore actively. This will remove the sem_t object in
+ // the system and allow others to create a different sem_t object with the
+ // same/ name.
+ static void Unlink(const char* name);
+
+ private:
+ sem_t* semaphore_;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_SCREEN_DRAWER_LOCK_POSIX_H_
diff --git a/webrtc/modules/desktop_capture/screen_drawer_mac.cc b/webrtc/modules/desktop_capture/screen_drawer_mac.cc
index c845929..90f7271 100644
--- a/webrtc/modules/desktop_capture/screen_drawer_mac.cc
+++ b/webrtc/modules/desktop_capture/screen_drawer_mac.cc
@@ -11,12 +11,14 @@
// TODO(zijiehe): Implement ScreenDrawerMac
#include "webrtc/modules/desktop_capture/screen_drawer.h"
+#include "webrtc/modules/desktop_capture/screen_drawer_lock_posix.h"
+#include "webrtc/rtc_base/ptr_util.h"
namespace webrtc {
// static
std::unique_ptr<ScreenDrawerLock> ScreenDrawerLock::Create() {
- return nullptr;
+ return rtc::MakeUnique<ScreenDrawerLockPosix>();
}
// static
diff --git a/webrtc/modules/desktop_capture/screen_drawer_unittest.cc b/webrtc/modules/desktop_capture/screen_drawer_unittest.cc
index a99395b..94404ee 100644
--- a/webrtc/modules/desktop_capture/screen_drawer_unittest.cc
+++ b/webrtc/modules/desktop_capture/screen_drawer_unittest.cc
@@ -10,16 +10,98 @@
#include "webrtc/modules/desktop_capture/screen_drawer.h"
+#include <atomic>
#include <stdint.h>
+#include "webrtc/rtc_base/checks.h"
+#include "webrtc/rtc_base/function_view.h"
#include "webrtc/rtc_base/logging.h"
#include "webrtc/rtc_base/random.h"
+#include "webrtc/rtc_base/platform_thread.h"
+#include "webrtc/rtc_base/ptr_util.h"
#include "webrtc/rtc_base/timeutils.h"
#include "webrtc/system_wrappers/include/sleep.h"
#include "webrtc/test/gtest.h"
+#if defined(WEBRTC_POSIX)
+#include "webrtc/modules/desktop_capture/screen_drawer_lock_posix.h"
+#endif
+
namespace webrtc {
+namespace {
+
+void TestScreenDrawerLock(
+ rtc::FunctionView<std::unique_ptr<ScreenDrawerLock>()> ctor) {
+ constexpr int kLockDurationMs = 100;
+
+ RTC_DCHECK(ctor);
+
+ std::atomic<bool> created(false);
+ std::atomic<bool> ready(false);
+
+ class Task {
+ public:
+ Task(std::atomic<bool>* created,
+ const std::atomic<bool>& ready,
+ rtc::FunctionView<std::unique_ptr<ScreenDrawerLock>()> ctor)
+ : created_(created),
+ ready_(ready),
+ ctor_(ctor) {}
+
+ ~Task() = default;
+
+ static void RunTask(void* me) {
+ Task* task = static_cast<Task*>(me);
+ std::unique_ptr<ScreenDrawerLock> lock = task->ctor_();
+ ASSERT_TRUE(!!lock);
+ task->created_->store(true);
+ // Wait for the main thread to get the signal of created_.
+ while (!task->ready_.load()) {
+ SleepMs(1);
+ }
+ // At this point, main thread should begin to create a second lock. Though
+ // it's still possible the second lock won't be created before the
+ // following sleep has been finished, the possibility will be
+ // significantly reduced.
+ const int64_t current_ms = rtc::TimeMillis();
+ // SleepMs() may return early. See
+ // https://cs.chromium.org/chromium/src/third_party/webrtc/system_wrappers/include/sleep.h?rcl=4a604c80cecce18aff6fc5e16296d04675312d83&l=20
+ // But we need to ensure at least 100 ms has been passed before unlocking
+ // |lock|.
+ while (rtc::TimeMillis() - current_ms < kLockDurationMs) {
+ SleepMs(kLockDurationMs - (rtc::TimeMillis() - current_ms));
+ }
+ }
+
+ private:
+ std::atomic<bool>* const created_;
+ const std::atomic<bool>& ready_;
+ const rtc::FunctionView<std::unique_ptr<ScreenDrawerLock>()> ctor_;
+ } task(&created, ready, ctor);
+
+ rtc::PlatformThread lock_thread(&Task::RunTask, &task, "lock_thread");
+ lock_thread.Start();
+
+ // Wait for the first lock in Task::RunTask() to be created.
+ // TODO(zijiehe): Find a better solution to wait for the creation of the first
+ // lock. See https://chromium-review.googlesource.com/c/607688/13/webrtc/modules/desktop_capture/screen_drawer_unittest.cc
+ while (!created.load()) {
+ SleepMs(1);
+ }
+
+ const int64_t start_ms = rtc::TimeMillis();
+ ready.store(true);
+ // This is unlikely to fail, but just in case current thread is too laggy and
+ // cause the SleepMs() in RunTask() to finish before we creating another lock.
+ ASSERT_GT(kLockDurationMs, rtc::TimeMillis() - start_ms);
+ ctor();
+ ASSERT_LE(kLockDurationMs, rtc::TimeMillis() - start_ms);
+ lock_thread.Stop();
+}
+
+} // namespace
+
// These are a set of manual test cases, as we do not have an automatical way to
// detect whether a ScreenDrawer on a certain platform works well without
// ScreenCapturer(s). So you may execute these test cases with
@@ -57,4 +139,21 @@
SleepMs(10000);
}
+TEST(ScreenDrawerTest, TwoScreenDrawerLocks) {
+#if defined(WEBRTC_POSIX)
+ // ScreenDrawerLockPosix won't be able to unlink the named semaphore. So use a
+ // different semaphore name here to avoid deadlock.
+ const char* semaphore_name = "GSDL8784541a812011e788ff67427b";
+ ScreenDrawerLockPosix::Unlink(semaphore_name);
+
+ TestScreenDrawerLock([semaphore_name]() {
+ return rtc::MakeUnique<ScreenDrawerLockPosix>(semaphore_name);
+ });
+#elif defined(WEBRTC_WIN)
+ TestScreenDrawerLock([]() {
+ return ScreenDrawerLock::Create();
+ });
+#endif
+}
+
} // namespace webrtc