blob: 0eaef1ef4cd6d8b0800101d23cf02d852b580469 [file] [log] [blame]
fdorayf9f6c1e92016-09-15 13:23:241// Copyright 2016 The Chromium Authors. All rights reserved.
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/files/file_descriptor_watcher_posix.h"
6
Lei Zhange3e126d782020-01-07 21:36:007#include <utility>
8
fdorayf9f6c1e92016-09-15 13:23:249#include "base/bind.h"
François Doray1e392d22019-01-29 16:00:1910#include "base/callback_helpers.h"
fdorayf9f6c1e92016-09-15 13:23:2411#include "base/memory/ptr_util.h"
Gabriel Charette19d2ae62018-04-10 14:10:5812#include "base/message_loop/message_pump_for_io.h"
Lei Zhange3e126d782020-01-07 21:36:0013#include "base/no_destructor.h"
fdorayf9f6c1e92016-09-15 13:23:2414#include "base/sequenced_task_runner.h"
15#include "base/single_thread_task_runner.h"
François Doray1e392d22019-01-29 16:00:1916#include "base/synchronization/waitable_event.h"
Carlos Caballerob25fe8472020-07-17 10:27:1717#include "base/task/current_thread.h"
fdorayf9f6c1e92016-09-15 13:23:2418#include "base/threading/sequenced_task_runner_handle.h"
19#include "base/threading/thread_checker.h"
20#include "base/threading/thread_local.h"
François Doray1e392d22019-01-29 16:00:1921#include "base/threading/thread_restrictions.h"
fdorayf9f6c1e92016-09-15 13:23:2422
23namespace base {
24
25namespace {
26
François Doray1e392d22019-01-29 16:00:1927// Per-thread FileDescriptorWatcher registration.
Lei Zhange3e126d782020-01-07 21:36:0028ThreadLocalPointer<FileDescriptorWatcher>& GetTlsFdWatcher() {
29 static NoDestructor<ThreadLocalPointer<FileDescriptorWatcher>> tls_fd_watcher;
30 return *tls_fd_watcher;
31}
fdorayf9f6c1e92016-09-15 13:23:2432
33} // namespace
34
fdorayf9f6c1e92016-09-15 13:23:2435class FileDescriptorWatcher::Controller::Watcher
Gabriel Charette19d2ae62018-04-10 14:10:5836 : public MessagePumpForIO::FdWatcher,
Carlos Caballerob25fe8472020-07-17 10:27:1737 public CurrentThread::DestructionObserver {
fdorayf9f6c1e92016-09-15 13:23:2438 public:
Gabriel Charette19d2ae62018-04-10 14:10:5839 Watcher(WeakPtr<Controller> controller, MessagePumpForIO::Mode mode, int fd);
fdorayf9f6c1e92016-09-15 13:23:2440 ~Watcher() override;
41
42 void StartWatching();
43
44 private:
45 friend class FileDescriptorWatcher;
46
Gabriel Charette19d2ae62018-04-10 14:10:5847 // MessagePumpForIO::FdWatcher:
fdorayf9f6c1e92016-09-15 13:23:2448 void OnFileCanReadWithoutBlocking(int fd) override;
49 void OnFileCanWriteWithoutBlocking(int fd) override;
50
Carlos Caballerob25fe8472020-07-17 10:27:1751 // CurrentThread::DestructionObserver:
fdorayf9f6c1e92016-09-15 13:23:2452 void WillDestroyCurrentMessageLoop() override;
53
François Doray1e392d22019-01-29 16:00:1954 // The MessagePumpForIO's watch handle (stops the watch when destroyed).
Gabriel Charette889b87c2018-05-11 22:23:3655 MessagePumpForIO::FdWatchController fd_watch_controller_;
fdorayf9f6c1e92016-09-15 13:23:2456
57 // Runs tasks on the sequence on which this was instantiated (i.e. the
58 // sequence on which the callback must run).
59 const scoped_refptr<SequencedTaskRunner> callback_task_runner_ =
60 SequencedTaskRunnerHandle::Get();
61
François Doray1e392d22019-01-29 16:00:1962 // The Controller that created this Watcher. This WeakPtr is bound to the
63 // |controller_| thread and can only be used by this Watcher to post back to
64 // |callback_task_runner_|.
fdorayf9f6c1e92016-09-15 13:23:2465 WeakPtr<Controller> controller_;
66
67 // Whether this Watcher is notified when |fd_| becomes readable or writable
68 // without blocking.
Gabriel Charette19d2ae62018-04-10 14:10:5869 const MessagePumpForIO::Mode mode_;
fdorayf9f6c1e92016-09-15 13:23:2470
71 // The watched file descriptor.
72 const int fd_;
73
74 // Except for the constructor, every method of this class must run on the same
François Doray1e392d22019-01-29 16:00:1975 // MessagePumpForIO thread.
fdorayf9f6c1e92016-09-15 13:23:2476 ThreadChecker thread_checker_;
77
78 // Whether this Watcher was registered as a DestructionObserver on the
François Doray1e392d22019-01-29 16:00:1979 // MessagePumpForIO thread.
fdorayf9f6c1e92016-09-15 13:23:2480 bool registered_as_destruction_observer_ = false;
81
82 DISALLOW_COPY_AND_ASSIGN(Watcher);
83};
84
85FileDescriptorWatcher::Controller::Watcher::Watcher(
86 WeakPtr<Controller> controller,
Gabriel Charette19d2ae62018-04-10 14:10:5887 MessagePumpForIO::Mode mode,
fdorayf9f6c1e92016-09-15 13:23:2488 int fd)
Gabriel Charette889b87c2018-05-11 22:23:3689 : fd_watch_controller_(FROM_HERE),
ssid5dd4fb32017-02-16 23:57:1790 controller_(controller),
91 mode_(mode),
92 fd_(fd) {
fdorayf9f6c1e92016-09-15 13:23:2493 DCHECK(callback_task_runner_);
94 thread_checker_.DetachFromThread();
95}
96
97FileDescriptorWatcher::Controller::Watcher::~Watcher() {
98 DCHECK(thread_checker_.CalledOnValidThread());
Carlos Caballerob25fe8472020-07-17 10:27:1799 CurrentIOThread::Get()->RemoveDestructionObserver(this);
fdorayf9f6c1e92016-09-15 13:23:24100}
101
102void FileDescriptorWatcher::Controller::Watcher::StartWatching() {
103 DCHECK(thread_checker_.CalledOnValidThread());
Carlos Caballerob25fe8472020-07-17 10:27:17104 DCHECK(CurrentIOThread::IsSet());
fdorayf9f6c1e92016-09-15 13:23:24105
Carlos Caballerob25fe8472020-07-17 10:27:17106 const bool watch_success = CurrentIOThread::Get()->WatchFileDescriptor(
107 fd_, false, mode_, &fd_watch_controller_, this);
François Doray1e392d22019-01-29 16:00:19108 DCHECK(watch_success) << "Failed to watch fd=" << fd_;
fdorayf9f6c1e92016-09-15 13:23:24109
110 if (!registered_as_destruction_observer_) {
Carlos Caballerob25fe8472020-07-17 10:27:17111 CurrentIOThread::Get()->AddDestructionObserver(this);
fdorayf9f6c1e92016-09-15 13:23:24112 registered_as_destruction_observer_ = true;
113 }
114}
115
116void FileDescriptorWatcher::Controller::Watcher::OnFileCanReadWithoutBlocking(
117 int fd) {
118 DCHECK_EQ(fd_, fd);
Gabriel Charette19d2ae62018-04-10 14:10:58119 DCHECK_EQ(MessagePumpForIO::WATCH_READ, mode_);
fdorayf9f6c1e92016-09-15 13:23:24120 DCHECK(thread_checker_.CalledOnValidThread());
121
122 // Run the callback on the sequence on which the watch was initiated.
tzik92b7a422017-04-11 15:00:44123 callback_task_runner_->PostTask(
124 FROM_HERE, BindOnce(&Controller::RunCallback, controller_));
fdorayf9f6c1e92016-09-15 13:23:24125}
126
127void FileDescriptorWatcher::Controller::Watcher::OnFileCanWriteWithoutBlocking(
128 int fd) {
129 DCHECK_EQ(fd_, fd);
Gabriel Charette19d2ae62018-04-10 14:10:58130 DCHECK_EQ(MessagePumpForIO::WATCH_WRITE, mode_);
fdorayf9f6c1e92016-09-15 13:23:24131 DCHECK(thread_checker_.CalledOnValidThread());
132
133 // Run the callback on the sequence on which the watch was initiated.
tzik92b7a422017-04-11 15:00:44134 callback_task_runner_->PostTask(
135 FROM_HERE, BindOnce(&Controller::RunCallback, controller_));
fdorayf9f6c1e92016-09-15 13:23:24136}
137
138void FileDescriptorWatcher::Controller::Watcher::
139 WillDestroyCurrentMessageLoop() {
140 DCHECK(thread_checker_.CalledOnValidThread());
141
François Doray1e392d22019-01-29 16:00:19142 if (callback_task_runner_->RunsTasksInCurrentSequence()) {
143 // |controller_| can be accessed directly when Watcher runs on the same
144 // thread.
145 controller_->watcher_.reset();
146 } else {
147 // If the Watcher and the Controller live on different threads, delete
148 // |this| synchronously. Pending tasks bound to an unretained Watcher* will
149 // not run since this loop is dead. The associated Controller still
150 // technically owns this via unique_ptr but it never uses it directly and
151 // will ultimately send it to this thread for deletion (and that also won't
152 // run since the loop being dead).
153 delete this;
154 }
fdorayf9f6c1e92016-09-15 13:23:24155}
156
Gabriel Charette19d2ae62018-04-10 14:10:58157FileDescriptorWatcher::Controller::Controller(MessagePumpForIO::Mode mode,
fdorayf9f6c1e92016-09-15 13:23:24158 int fd,
kylecharb2695fc2019-04-24 14:51:20159 const RepeatingClosure& callback)
fdorayf9f6c1e92016-09-15 13:23:24160 : callback_(callback),
Lei Zhange3e126d782020-01-07 21:36:00161 io_thread_task_runner_(GetTlsFdWatcher().Get()->io_thread_task_runner()) {
fdorayf9f6c1e92016-09-15 13:23:24162 DCHECK(!callback_.is_null());
Alexander Timin6247f6a91a2018-10-27 15:40:10163 DCHECK(io_thread_task_runner_);
Jeremy Roman9532f252017-08-16 23:27:24164 watcher_ = std::make_unique<Watcher>(weak_factory_.GetWeakPtr(), mode, fd);
fdorayf9f6c1e92016-09-15 13:23:24165 StartWatching();
166}
167
François Doray1e392d22019-01-29 16:00:19168FileDescriptorWatcher::Controller::~Controller() {
169 DCHECK(sequence_checker_.CalledOnValidSequence());
170
171 if (io_thread_task_runner_->BelongsToCurrentThread()) {
172 // If the MessagePumpForIO and the Controller live on the same thread.
173 watcher_.reset();
174 } else {
175 // Synchronously wait until |watcher_| is deleted on the MessagePumpForIO
176 // thread. This ensures that the file descriptor is never accessed after
177 // this destructor returns.
178 //
Gabriel Charette30cdf0e52019-07-16 19:28:21179 // Use a ScopedClosureRunner to ensure that |done| is signaled even if the
François Doray1e392d22019-01-29 16:00:19180 // thread doesn't run any more tasks (if PostTask returns true, it means
181 // that the task was queued, but it doesn't mean that a RunLoop will run the
182 // task before the queue is deleted).
183 //
184 // We considered associating "generations" to file descriptors to avoid the
185 // synchronous wait. For example, if the IO thread gets a "cancel" for fd=6,
186 // generation=1 after getting a "start watching" for fd=6, generation=2, it
187 // can ignore the "Cancel". However, "generations" didn't solve this race:
188 //
189 // T1 (client) Start watching fd = 6 with WatchReadable()
190 // Stop watching fd = 6
191 // Close fd = 6
192 // Open a new file, fd = 6 gets reused.
193 // T2 (io) Watcher::StartWatching()
194 // Incorrectly starts watching fd = 6 which now refers to a
195 // different file than when WatchReadable() was called.
196 WaitableEvent done;
197 io_thread_task_runner_->PostTask(
198 FROM_HERE, BindOnce(
199 [](Watcher* watcher, ScopedClosureRunner closure) {
200 // Since |watcher| is a raw pointer, it isn't deleted
201 // if this callback is deleted before it gets to run.
202 delete watcher;
203 // |closure| runs at the end of this scope.
204 },
205 Unretained(watcher_.release()),
206 ScopedClosureRunner(BindOnce(&WaitableEvent::Signal,
207 Unretained(&done)))));
208 ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow;
209 done.Wait();
210 }
211
212 // Since WeakPtrs are invalidated by the destructor, any pending RunCallback()
213 // won't be invoked after this returns.
214}
215
fdorayf9f6c1e92016-09-15 13:23:24216void FileDescriptorWatcher::Controller::StartWatching() {
217 DCHECK(sequence_checker_.CalledOnValidSequence());
François Doray1e392d22019-01-29 16:00:19218 if (io_thread_task_runner_->BelongsToCurrentThread()) {
219 // If the MessagePumpForIO and the Controller live on the same thread.
220 watcher_->StartWatching();
221 } else {
222 // It is safe to use Unretained() below because |watcher_| can only be
223 // deleted by a delete task posted to |io_thread_task_runner_| by this
224 // Controller's destructor. Since this delete task hasn't been posted yet,
225 // it can't run before the task posted below.
226 io_thread_task_runner_->PostTask(
227 FROM_HERE,
228 BindOnce(&Watcher::StartWatching, Unretained(watcher_.get())));
229 }
fdorayf9f6c1e92016-09-15 13:23:24230}
231
232void FileDescriptorWatcher::Controller::RunCallback() {
233 DCHECK(sequence_checker_.CalledOnValidSequence());
234
235 WeakPtr<Controller> weak_this = weak_factory_.GetWeakPtr();
236
237 callback_.Run();
238
239 // If |this| wasn't deleted, re-enable the watch.
240 if (weak_this)
241 StartWatching();
242}
243
244FileDescriptorWatcher::FileDescriptorWatcher(
Alexander Timin6247f6a91a2018-10-27 15:40:10245 scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner)
246 : io_thread_task_runner_(std::move(io_thread_task_runner)) {
Lei Zhange3e126d782020-01-07 21:36:00247 DCHECK(!GetTlsFdWatcher().Get());
248 GetTlsFdWatcher().Set(this);
fdorayf9f6c1e92016-09-15 13:23:24249}
250
251FileDescriptorWatcher::~FileDescriptorWatcher() {
Lei Zhange3e126d782020-01-07 21:36:00252 GetTlsFdWatcher().Set(nullptr);
fdorayf9f6c1e92016-09-15 13:23:24253}
254
255std::unique_ptr<FileDescriptorWatcher::Controller>
kylecharb2695fc2019-04-24 14:51:20256FileDescriptorWatcher::WatchReadable(int fd, const RepeatingClosure& callback) {
Gabriel Charette19d2ae62018-04-10 14:10:58257 return WrapUnique(new Controller(MessagePumpForIO::WATCH_READ, fd, callback));
fdorayf9f6c1e92016-09-15 13:23:24258}
259
260std::unique_ptr<FileDescriptorWatcher::Controller>
kylecharb2695fc2019-04-24 14:51:20261FileDescriptorWatcher::WatchWritable(int fd, const RepeatingClosure& callback) {
fdorayf9f6c1e92016-09-15 13:23:24262 return WrapUnique(
Gabriel Charette19d2ae62018-04-10 14:10:58263 new Controller(MessagePumpForIO::WATCH_WRITE, fd, callback));
fdorayf9f6c1e92016-09-15 13:23:24264}
265
Francois Dorayc1868292018-12-06 02:29:43266#if DCHECK_IS_ON()
267void FileDescriptorWatcher::AssertAllowed() {
Lei Zhange3e126d782020-01-07 21:36:00268 DCHECK(GetTlsFdWatcher().Get());
Francois Dorayc1868292018-12-06 02:29:43269}
270#endif
271
fdorayf9f6c1e92016-09-15 13:23:24272} // namespace base