xref: /aosp_15_r20/external/cronet/base/synchronization/waitable_event_watcher_mac.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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