1 // Copyright 2017 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/waitable_event_watcher.h"
6
7 #include "base/apple/scoped_dispatch_object.h"
8 #include "base/functional/bind.h"
9 #include "base/functional/callback.h"
10
11 namespace base {
12
13 struct WaitableEventWatcher::Storage {
14 // A TYPE_MACH_RECV dispatch source on |receive_right_|. When a receive event
15 // is delivered, the message queue will be peeked and the bound |callback_|
16 // may be run. This will be null if nothing is currently being watched.
17 apple::ScopedDispatchObject<dispatch_source_t> dispatch_source;
18 };
19
WaitableEventWatcher()20 WaitableEventWatcher::WaitableEventWatcher()
21 : storage_(std::make_unique<Storage>()), weak_ptr_factory_(this) {}
22
~WaitableEventWatcher()23 WaitableEventWatcher::~WaitableEventWatcher() {
24 StopWatching();
25 }
26
StartWatching(WaitableEvent * event,EventCallback callback,scoped_refptr<SequencedTaskRunner> task_runner)27 bool WaitableEventWatcher::StartWatching(
28 WaitableEvent* event,
29 EventCallback callback,
30 scoped_refptr<SequencedTaskRunner> task_runner) {
31 DCHECK(task_runner->RunsTasksInCurrentSequence());
32 DCHECK(!storage_->dispatch_source ||
33 dispatch_source_testcancel(storage_->dispatch_source.get()));
34
35 // Keep a reference to the receive right, so that if the event is deleted
36 // out from under the watcher, a signal can still be observed.
37 receive_right_ = event->receive_right_;
38
39 // UnsafeDanglingUntriaged triggered by test:
40 // WaitableEventWatcherDeletionTest.SignalAndDelete
41 // TODO(https://crbug.com/1380714): Remove `UnsafeDanglingUntriaged`
42 callback_ =
43 BindOnce(std::move(callback), base::UnsafeDanglingUntriaged(event));
44
45 // Use the global concurrent queue here, since it is only used to thunk
46 // to the real callback on the target task runner.
47 storage_->dispatch_source.reset(dispatch_source_create(
48 DISPATCH_SOURCE_TYPE_MACH_RECV, receive_right_->Name(), 0,
49 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)));
50
51 // Locals for capture by the block. Accessing anything through the |this| or
52 // |event| pointers is not safe, since either may have been deleted by the
53 // time the handler block is invoked.
54 WeakPtr<WaitableEventWatcher> weak_this = weak_ptr_factory_.GetWeakPtr();
55 const bool auto_reset =
56 event->policy_ == WaitableEvent::ResetPolicy::AUTOMATIC;
57 dispatch_source_t source = storage_->dispatch_source.get();
58 mach_port_t name = receive_right_->Name();
59
60 dispatch_source_set_event_handler(storage_->dispatch_source.get(), ^{
61 // For automatic-reset events, only fire the callback if this watcher
62 // can claim/dequeue the event. For manual-reset events, all watchers can
63 // be called back.
64 if (auto_reset && !WaitableEvent::PeekPort(name, true)) {
65 return;
66 }
67
68 // The event has been consumed. A watcher is one-shot, so cancel the
69 // source to prevent receiving future event signals.
70 dispatch_source_cancel(source);
71
72 task_runner->PostTask(
73 FROM_HERE, BindOnce(&WaitableEventWatcher::InvokeCallback, weak_this));
74 });
75 dispatch_resume(storage_->dispatch_source.get());
76
77 return true;
78 }
79
StopWatching()80 void WaitableEventWatcher::StopWatching() {
81 callback_.Reset();
82 receive_right_ = nullptr;
83 if (storage_->dispatch_source) {
84 dispatch_source_cancel(storage_->dispatch_source.get());
85 storage_->dispatch_source.reset();
86 }
87 }
88
InvokeCallback()89 void WaitableEventWatcher::InvokeCallback() {
90 // The callback can be null if StopWatching() is called between signaling
91 // and the |callback_| getting run on the target task runner.
92 if (callback_.is_null()) {
93 return;
94 }
95 storage_->dispatch_source.reset();
96 receive_right_ = nullptr;
97 std::move(callback_).Run();
98 }
99
100 } // namespace base
101