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 callback_ = BindOnce(std::move(callback), event);
40
41 // Use the global concurrent queue here, since it is only used to thunk
42 // to the real callback on the target task runner.
43 storage_->dispatch_source.reset(dispatch_source_create(
44 DISPATCH_SOURCE_TYPE_MACH_RECV, receive_right_->Name(), 0,
45 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)));
46
47 // Locals for capture by the block. Accessing anything through the |this| or
48 // |event| pointers is not safe, since either may have been deleted by the
49 // time the handler block is invoked.
50 WeakPtr<WaitableEventWatcher> weak_this = weak_ptr_factory_.GetWeakPtr();
51 const bool auto_reset =
52 event->policy_ == WaitableEvent::ResetPolicy::AUTOMATIC;
53 dispatch_source_t source = storage_->dispatch_source.get();
54 mach_port_t name = receive_right_->Name();
55
56 dispatch_source_set_event_handler(storage_->dispatch_source.get(), ^{
57 // For automatic-reset events, only fire the callback if this watcher
58 // can claim/dequeue the event. For manual-reset events, all watchers can
59 // be called back.
60 if (auto_reset && !WaitableEvent::PeekPort(name, true)) {
61 return;
62 }
63
64 // The event has been consumed. A watcher is one-shot, so cancel the
65 // source to prevent receiving future event signals.
66 dispatch_source_cancel(source);
67
68 task_runner->PostTask(
69 FROM_HERE, BindOnce(&WaitableEventWatcher::InvokeCallback, weak_this));
70 });
71 dispatch_resume(storage_->dispatch_source.get());
72
73 return true;
74 }
75
StopWatching()76 void WaitableEventWatcher::StopWatching() {
77 callback_.Reset();
78 receive_right_ = nullptr;
79 if (storage_->dispatch_source) {
80 dispatch_source_cancel(storage_->dispatch_source.get());
81 storage_->dispatch_source.reset();
82 }
83 }
84
InvokeCallback()85 void WaitableEventWatcher::InvokeCallback() {
86 // The callback can be null if StopWatching() is called between signaling
87 // and the |callback_| getting run on the target task runner.
88 if (callback_.is_null()) {
89 return;
90 }
91 storage_->dispatch_source.reset();
92 receive_right_ = nullptr;
93 std::move(callback_).Run();
94 }
95
96 } // namespace base
97