|  | // Copyright 2017 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/synchronization/waitable_event_watcher.h" | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/callback.h" | 
|  | #include "base/threading/sequenced_task_runner_handle.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | WaitableEventWatcher::WaitableEventWatcher() : weak_ptr_factory_(this) {} | 
|  |  | 
|  | WaitableEventWatcher::~WaitableEventWatcher() { | 
|  | StopWatching(); | 
|  | } | 
|  |  | 
|  | bool WaitableEventWatcher::StartWatching( | 
|  | WaitableEvent* event, | 
|  | EventCallback callback, | 
|  | scoped_refptr<SequencedTaskRunner> task_runner) { | 
|  | DCHECK(task_runner->RunsTasksInCurrentSequence()); | 
|  | DCHECK(!source_ || dispatch_source_testcancel(source_)); | 
|  |  | 
|  | // Keep a reference to the receive right, so that if the event is deleted | 
|  | // out from under the watcher, a signal can still be observed. | 
|  | receive_right_ = event->receive_right_; | 
|  |  | 
|  | callback_ = BindOnce(std::move(callback), event); | 
|  |  | 
|  | // Locals for capture by the block. Accessing anything through the |this| or | 
|  | // |event| pointers is not safe, since either may have been deleted by the | 
|  | // time the handler block is invoked. | 
|  | WeakPtr<WaitableEventWatcher> weak_this = weak_ptr_factory_.GetWeakPtr(); | 
|  | const bool auto_reset = | 
|  | event->policy_ == WaitableEvent::ResetPolicy::AUTOMATIC; | 
|  |  | 
|  | // Auto-reset events always use a dispatch source. Manual-reset events | 
|  | // only do so if dispatch provides reliable delivery, otherwise a manual | 
|  | // watcher list is used. | 
|  | if (!WaitableEvent::UseSlowWatchList(event->policy_)) { | 
|  | // Use the global concurrent queue here, since it is only used to thunk | 
|  | // to the real callback on the target task runner. | 
|  | source_.reset(dispatch_source_create( | 
|  | DISPATCH_SOURCE_TYPE_MACH_RECV, receive_right_->Name(), 0, | 
|  | dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))); | 
|  |  | 
|  | // Additional locals for block capture. | 
|  | dispatch_source_t source = source_.get(); | 
|  | mach_port_t name = receive_right_->Name(); | 
|  |  | 
|  | dispatch_source_set_event_handler(source_, ^{ | 
|  | // For automatic-reset events, only fire the callback if this watcher | 
|  | // can claim/dequeue the event. For manual-reset events, all watchers can | 
|  | // be called back. | 
|  | if (auto_reset && !WaitableEvent::PeekPort(name, true)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // The event has been consumed. A watcher is one-shot, so cancel the | 
|  | // source to prevent receiving future event signals. | 
|  | dispatch_source_cancel(source); | 
|  |  | 
|  | task_runner->PostTask( | 
|  | FROM_HERE, | 
|  | BindOnce(&WaitableEventWatcher::InvokeCallback, weak_this)); | 
|  | }); | 
|  | dispatch_resume(source_); | 
|  | } else { | 
|  | // The |event->watch_list_| closures can be run from any thread, so bind | 
|  | // the callback as an invocation of PostTask. | 
|  | OnceClosure watcher = | 
|  | BindOnce(IgnoreResult(&TaskRunner::PostTask), task_runner, FROM_HERE, | 
|  | BindOnce(&WaitableEventWatcher::InvokeCallback, weak_this)); | 
|  |  | 
|  | // Hold an additional reference to the ReceiveRight, in case |watcher| | 
|  | // runs and deletes the event while the lock is held. | 
|  | // Hold the lock for the duration of IsSignaled() so that if Signal() | 
|  | // is called by another thread, it waits for this to be added to the | 
|  | // watch list. | 
|  | scoped_refptr<WaitableEvent::ReceiveRight> receive_right(receive_right_); | 
|  | AutoLock lock(receive_right->SlowWatchList()->lock); | 
|  | if (event->IsSignaled()) { | 
|  | std::move(watcher).Run(); | 
|  | return true; | 
|  | } | 
|  | receive_right_->SlowWatchList()->list.push_back(std::move(watcher)); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void WaitableEventWatcher::StopWatching() { | 
|  | callback_.Reset(); | 
|  | receive_right_ = nullptr; | 
|  | if (source_) { | 
|  | dispatch_source_cancel(source_); | 
|  | source_.reset(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void WaitableEventWatcher::InvokeCallback() { | 
|  | // The callback can be null if StopWatching() is called between signaling | 
|  | // and the |callback_| getting run on the target task runner. | 
|  | if (callback_.is_null()) | 
|  | return; | 
|  | source_.reset(); | 
|  | receive_right_ = nullptr; | 
|  | std::move(callback_).Run(); | 
|  | } | 
|  |  | 
|  | }  // namespace base |