1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/synchronization/cancelable_event.h"
6
7 #include <errno.h>
8 #include <semaphore.h>
9
10 #include <tuple>
11
12 #include "base/check.h"
13 #include "base/dcheck_is_on.h"
14 #include "base/logging.h"
15 #include "base/notreached.h"
16 #include "base/posix/eintr_wrapper.h"
17 #include "base/time/time.h"
18
19 namespace base {
20
21 namespace {
22 // Translates a base::TimeDelta (relative to now) to timespec containing
23 // that position in time relative to unix epoch
TimeDeltaToAbsTimeSpec(base::TimeDelta time_delta)24 struct timespec TimeDeltaToAbsTimeSpec(base::TimeDelta time_delta) {
25 struct timespec now;
26 clock_gettime(CLOCK_REALTIME, &now);
27 struct timespec offset = time_delta.ToTimeSpec();
28 now.tv_sec += offset.tv_sec;
29 now.tv_nsec += offset.tv_nsec;
30 if (now.tv_nsec >= Time::kNanosecondsPerSecond) {
31 now.tv_sec++;
32 now.tv_nsec -= Time::kNanosecondsPerSecond;
33 }
34 return now;
35 }
36 } // namespace
37
CancelableEvent()38 CancelableEvent::CancelableEvent() {
39 int result = sem_init(&native_handle_, 0, 0U);
40 CHECK_EQ(result, 0);
41 }
42
~CancelableEvent()43 CancelableEvent::~CancelableEvent() {
44 int result = sem_destroy(&native_handle_);
45 CHECK_EQ(result, 0);
46 }
47
SignalImpl()48 void CancelableEvent::SignalImpl() {
49 int result;
50 #if DCHECK_IS_ON()
51 int sem_value = 0;
52 result = sem_getvalue(&native_handle_, &sem_value);
53 CHECK_EQ(result, 0);
54 DCHECK_EQ(sem_value, 0);
55 #endif
56 result = sem_post(&native_handle_);
57 CHECK_EQ(result, 0);
58 }
59
CancelImpl()60 bool CancelableEvent::CancelImpl() {
61 int result = sem_trywait(&native_handle_);
62 if (result == -1 && errno == EAGAIN) {
63 return false;
64 }
65 if (result == 0) {
66 return true;
67 }
68
69 PCHECK(false);
70 }
71
TimedWaitImpl(TimeDelta timeout)72 bool CancelableEvent::TimedWaitImpl(TimeDelta timeout) {
73 int result;
74 if (timeout.is_max()) {
75 result = HANDLE_EINTR(sem_wait(&native_handle_));
76 } else {
77 // Compute the time for the end of the timeout.
78 const struct timespec ts = TimeDeltaToAbsTimeSpec(timeout);
79
80 // Wait for semaphore to be signalled or to timeout.
81 result = HANDLE_EINTR(sem_timedwait(&native_handle_, &ts));
82 }
83 if (result == 0) {
84 return true; // The semaphore was signalled.
85 }
86 if (result == -1 && errno == ETIMEDOUT) {
87 // Timed out while waiting for semaphore.
88 return false;
89 }
90 PCHECK(false);
91 }
92
93 } // namespace base
94