• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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