Chromium Code Reviews| Index: ui/gfx/shared_event.cc |
| diff --git a/ui/gfx/shared_event.cc b/ui/gfx/shared_event.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b19d0329df127f934081e32bc2456e078f5aa9a4 |
| --- /dev/null |
| +++ b/ui/gfx/shared_event.cc |
| @@ -0,0 +1,120 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "base/atomicops.h" |
| +#include "base/macros.h" |
| +#include "base/process/memory.h" |
| +#include "base/time/time.h" |
| +#include "ui/gfx/gfx_export.h" |
| +#include "ui/gfx/shared_event.h" |
| + |
| +#if defined(OS_LINUX) || defined(OS_ANDROID) |
| +#include <linux/futex.h> |
| +#include <stdint.h> |
| +#include <sys/syscall.h> |
| +#else |
| +#include "base/threading/platform_thread.h" |
| +#endif |
| + |
| +namespace gfx { |
| +namespace { |
| + |
| +enum { NOT_SIGNALED = 0, WAITING, SIGNALED }; |
| + |
| +constexpr size_t kSharedMemorySize = sizeof(int32_t); |
|
ericrk
2017/03/10 03:04:39
I'm curious about the size efficiency here. From m
reveman
2017/03/13 12:33:05
Correct. Each fence will end up using one page of
|
| + |
| +} // namespace |
| + |
| +SharedEvent::SharedEvent() { |
| + bool rv = shared_memory_.CreateAndMapAnonymous(kSharedMemorySize); |
| + CHECK(rv); |
| +} |
| + |
| +SharedEvent::SharedEvent(const SharedEventHandle& handle) |
| + : shared_memory_(handle, false) { |
| + bool rv = shared_memory_.Map(kSharedMemorySize); |
| + CHECK(rv); |
| +} |
| + |
| +// static |
| +SharedEventHandle SharedEvent::CreateForProcess(base::ProcessHandle process) { |
| + base::SharedMemory shared_memory; |
| + if (!shared_memory.CreateAnonymous(kSharedMemorySize)) |
| + return SharedEventHandle(); |
| + base::SharedMemoryHandle handle; |
| + shared_memory.GiveToProcess(process, &handle); |
| + return handle; |
| +} |
| + |
| +// static |
| +SharedEventHandle SharedEvent::DuplicateHandle( |
| + const SharedEventHandle& handle) { |
| + return base::SharedMemory::DuplicateHandle(handle); |
| +} |
| + |
| +void SharedEvent::Reset() { |
| + // Change state to un-signaled iff event is currently in signaled state. |
| + base::subtle::NoBarrier_CompareAndSwap(uaddr(), SIGNALED, NOT_SIGNALED); |
| +} |
| + |
| +void SharedEvent::Signal() { |
| + // Change state to signaled and return early unless waiters exist that need |
| + // to be woken up. |
| + if (base::subtle::Release_CompareAndSwap(uaddr(), NOT_SIGNALED, SIGNALED) != |
| + WAITING) |
| + return; |
| + |
| + // Unconditionally change state to signaled before waking up waiters. |
| + base::subtle::Release_Store(uaddr(), SIGNALED); |
| + |
| +#if defined(OS_LINUX) || defined(OS_ANDROID) |
| + // Perform non-blocking futex operation that will wake up all waiters. |
| + int rv = syscall(SYS_futex, uaddr(), FUTEX_WAKE, INT_MAX, NULL, NULL, 0); |
| + PLOG_IF(ERROR, rv < 0) << "futex"; |
| +#endif |
| +} |
| + |
| +bool SharedEvent::IsSignaled() { |
| + return base::subtle::Acquire_Load(uaddr()) == SIGNALED; |
| +} |
| + |
| +bool SharedEvent::Wait(const base::TimeDelta& max_time) { |
| +#if defined(OS_LINUX) || defined(OS_ANDROID) |
| + struct timespec timeout = max_time.ToTimeSpec(); |
| +#endif |
| + |
| + // Wait for signaled state by repeatadly attempting to enter waiting state |
| + // until it fails because current state is signaled. |
| + while (base::subtle::Acquire_CompareAndSwap(uaddr(), NOT_SIGNALED, WAITING) != |
| + SIGNALED) { |
| +#if defined(OS_LINUX) || defined(OS_ANDROID) |
| + // Perform futex operation that will block until woken up or |timeout| has |
| + // expired. Note: If the futex value does not match waiting state, then the |
| + // call fails immediately with the error EAGAIN. |
| + if (syscall(SYS_futex, uaddr(), FUTEX_WAIT, WAITING, &timeout, NULL, 0)) { |
| + PLOG_IF(ERROR, errno != EAGAIN && errno != ETIMEDOUT) << "futex"; |
| + return false; |
| + } |
| + // Protect against spurious wake-ups by checking state again. Reset |
| + // timeout to prevent this function from blocking significantly longer |
| + // than |max_time|. |
| + timeout = {0}; |
| +#else |
| + base::PlatformThread::YieldCurrentThread(); |
| + return false; |
| +#endif |
| + } |
| + |
| + return true; |
| +} |
| + |
| +SharedEventHandle SharedEvent::GetHandle() const { |
| + return shared_memory_.handle(); |
| +} |
| + |
| +void SharedEvent::Close() { |
| + shared_memory_.Close(); |
| +} |
| + |
| +} // namespace gfx |