Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(793)

Side by Side Diff: base/files/file_path_watcher_linux.cc

Issue 2667513003: Remove some LazyInstance use in base/ (Closed)
Patch Set: . Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/files/file_path_watcher.h" 5 #include "base/files/file_path_watcher.h"
6 6
7 #include <errno.h> 7 #include <errno.h>
8 #include <stddef.h> 8 #include <stddef.h>
9 #include <string.h> 9 #include <string.h>
10 #include <sys/inotify.h> 10 #include <sys/inotify.h>
11 #include <sys/ioctl.h> 11 #include <sys/ioctl.h>
12 #include <sys/select.h> 12 #include <sys/select.h>
13 #include <unistd.h> 13 #include <unistd.h>
14 14
15 #include <algorithm> 15 #include <algorithm>
16 #include <map> 16 #include <map>
17 #include <memory> 17 #include <memory>
18 #include <set> 18 #include <set>
19 #include <utility> 19 #include <utility>
20 #include <vector> 20 #include <vector>
21 21
22 #include "base/bind.h" 22 #include "base/bind.h"
23 #include "base/containers/hash_tables.h" 23 #include "base/containers/hash_tables.h"
24 #include "base/files/file_enumerator.h" 24 #include "base/files/file_enumerator.h"
25 #include "base/files/file_path.h" 25 #include "base/files/file_path.h"
26 #include "base/files/file_util.h" 26 #include "base/files/file_util.h"
27 #include "base/lazy_instance.h"
28 #include "base/location.h" 27 #include "base/location.h"
29 #include "base/logging.h" 28 #include "base/logging.h"
30 #include "base/macros.h" 29 #include "base/macros.h"
31 #include "base/memory/ptr_util.h" 30 #include "base/memory/ptr_util.h"
32 #include "base/memory/weak_ptr.h" 31 #include "base/memory/weak_ptr.h"
33 #include "base/posix/eintr_wrapper.h" 32 #include "base/posix/eintr_wrapper.h"
34 #include "base/single_thread_task_runner.h" 33 #include "base/single_thread_task_runner.h"
35 #include "base/stl_util.h" 34 #include "base/stl_util.h"
36 #include "base/synchronization/lock.h" 35 #include "base/synchronization/lock.h"
37 #include "base/threading/sequenced_task_runner_handle.h" 36 #include "base/threading/sequenced_task_runner_handle.h"
(...skipping 18 matching lines...) Expand all
56 // change. Returns kInvalidWatch on failure. 55 // change. Returns kInvalidWatch on failure.
57 Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher); 56 Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher);
58 57
59 // Remove |watch| if it's valid. 58 // Remove |watch| if it's valid.
60 void RemoveWatch(Watch watch, FilePathWatcherImpl* watcher); 59 void RemoveWatch(Watch watch, FilePathWatcherImpl* watcher);
61 60
62 // Callback for InotifyReaderTask. 61 // Callback for InotifyReaderTask.
63 void OnInotifyEvent(const inotify_event* event); 62 void OnInotifyEvent(const inotify_event* event);
64 63
65 private: 64 private:
66 friend struct DefaultLazyInstanceTraits<InotifyReader>; 65 friend InotifyReader* GetInotifyReader();
67
68 typedef std::set<FilePathWatcherImpl*> WatcherSet; 66 typedef std::set<FilePathWatcherImpl*> WatcherSet;
69 67
70 InotifyReader(); 68 InotifyReader();
71 // There is no destructor because |g_inotify_reader| is a 69 // There is no destructor because GetInotifyReader() is leaky. Having a
72 // base::LazyInstace::Leaky object. Having a destructor causes build 70 // destructor causes build issues with GCC 6 (http://crbug.com/636346).
73 // issues with GCC 6 (http://crbug.com/636346).
74 71
75 // We keep track of which delegates want to be notified on which watches. 72 // We keep track of which delegates want to be notified on which watches.
76 hash_map<Watch, WatcherSet> watchers_; 73 hash_map<Watch, WatcherSet> watchers_;
77 74
78 // Lock to protect watchers_. 75 // Lock to protect watchers_.
79 Lock lock_; 76 Lock lock_;
80 77
81 // Separate thread on which we run blocking read for inotify events. 78 // Separate thread on which we run blocking read for inotify events.
82 Thread thread_; 79 Thread thread_;
83 80
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
236 while (i < bytes_read) { 233 while (i < bytes_read) {
237 inotify_event* event = reinterpret_cast<inotify_event*>(&buffer[i]); 234 inotify_event* event = reinterpret_cast<inotify_event*>(&buffer[i]);
238 size_t event_size = sizeof(inotify_event) + event->len; 235 size_t event_size = sizeof(inotify_event) + event->len;
239 DCHECK(i + event_size <= static_cast<size_t>(bytes_read)); 236 DCHECK(i + event_size <= static_cast<size_t>(bytes_read));
240 reader->OnInotifyEvent(event); 237 reader->OnInotifyEvent(event);
241 i += event_size; 238 i += event_size;
242 } 239 }
243 } 240 }
244 } 241 }
245 242
246 static LazyInstance<InotifyReader>::Leaky g_inotify_reader = 243 InotifyReader* GetInotifyReader() {
247 LAZY_INSTANCE_INITIALIZER; 244 static auto inotify_reader = new InotifyReader();
245 return inotify_reader;
246 }
248 247
249 InotifyReader::InotifyReader() 248 InotifyReader::InotifyReader()
250 : thread_("inotify_reader"), 249 : thread_("inotify_reader"),
251 inotify_fd_(inotify_init()), 250 inotify_fd_(inotify_init()),
252 valid_(false) { 251 valid_(false) {
253 if (inotify_fd_ < 0) 252 if (inotify_fd_ < 0)
254 PLOG(ERROR) << "inotify_init() failed"; 253 PLOG(ERROR) << "inotify_init() failed";
255 254
256 if (inotify_fd_ >= 0 && thread_.Start()) { 255 if (inotify_fd_ >= 0 && thread_.Start()) {
257 thread_.task_runner()->PostTask( 256 thread_.task_runner()->PostTask(
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after
451 return; 450 return;
452 } 451 }
453 452
454 DCHECK(task_runner()->RunsTasksOnCurrentThread()); 453 DCHECK(task_runner()->RunsTasksOnCurrentThread());
455 DCHECK(!is_cancelled()); 454 DCHECK(!is_cancelled());
456 455
457 set_cancelled(); 456 set_cancelled();
458 callback_.Reset(); 457 callback_.Reset();
459 458
460 for (size_t i = 0; i < watches_.size(); ++i) 459 for (size_t i = 0; i < watches_.size(); ++i)
461 g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); 460 GetInotifyReader()->RemoveWatch(watches_[i].watch, this);
462 watches_.clear(); 461 watches_.clear();
463 target_.clear(); 462 target_.clear();
464 RemoveRecursiveWatches(); 463 RemoveRecursiveWatches();
465 } 464 }
466 465
467 void FilePathWatcherImpl::UpdateWatches() { 466 void FilePathWatcherImpl::UpdateWatches() {
468 // Ensure this runs on the task_runner() exclusively in order to avoid 467 // Ensure this runs on the task_runner() exclusively in order to avoid
469 // concurrency issues. 468 // concurrency issues.
470 DCHECK(task_runner()->RunsTasksOnCurrentThread()); 469 DCHECK(task_runner()->RunsTasksOnCurrentThread());
471 DCHECK(HasValidWatchVector()); 470 DCHECK(HasValidWatchVector());
472 471
473 // Walk the list of watches and update them as we go. 472 // Walk the list of watches and update them as we go.
474 FilePath path(FILE_PATH_LITERAL("/")); 473 FilePath path(FILE_PATH_LITERAL("/"));
475 for (size_t i = 0; i < watches_.size(); ++i) { 474 for (size_t i = 0; i < watches_.size(); ++i) {
476 WatchEntry& watch_entry = watches_[i]; 475 WatchEntry& watch_entry = watches_[i];
477 InotifyReader::Watch old_watch = watch_entry.watch; 476 InotifyReader::Watch old_watch = watch_entry.watch;
478 watch_entry.watch = InotifyReader::kInvalidWatch; 477 watch_entry.watch = InotifyReader::kInvalidWatch;
479 watch_entry.linkname.clear(); 478 watch_entry.linkname.clear();
480 watch_entry.watch = g_inotify_reader.Get().AddWatch(path, this); 479 watch_entry.watch = GetInotifyReader()->AddWatch(path, this);
481 if (watch_entry.watch == InotifyReader::kInvalidWatch) { 480 if (watch_entry.watch == InotifyReader::kInvalidWatch) {
482 // Ignore the error code (beyond symlink handling) to attempt to add 481 // Ignore the error code (beyond symlink handling) to attempt to add
483 // watches on accessible children of unreadable directories. Note that 482 // watches on accessible children of unreadable directories. Note that
484 // this is a best-effort attempt; we may not catch events in this 483 // this is a best-effort attempt; we may not catch events in this
485 // scenario. 484 // scenario.
486 if (IsLink(path)) 485 if (IsLink(path))
487 AddWatchForBrokenSymlink(path, &watch_entry); 486 AddWatchForBrokenSymlink(path, &watch_entry);
488 } 487 }
489 if (old_watch != watch_entry.watch) 488 if (old_watch != watch_entry.watch)
490 g_inotify_reader.Get().RemoveWatch(old_watch, this); 489 GetInotifyReader()->RemoveWatch(old_watch, this);
491 path = path.Append(watch_entry.subdir); 490 path = path.Append(watch_entry.subdir);
492 } 491 }
493 492
494 UpdateRecursiveWatches(InotifyReader::kInvalidWatch, 493 UpdateRecursiveWatches(InotifyReader::kInvalidWatch,
495 false /* is directory? */); 494 false /* is directory? */);
496 } 495 }
497 496
498 void FilePathWatcherImpl::UpdateRecursiveWatches( 497 void FilePathWatcherImpl::UpdateRecursiveWatches(
499 InotifyReader::Watch fired_watch, 498 InotifyReader::Watch fired_watch,
500 bool is_dir) { 499 bool is_dir) {
(...skipping 25 matching lines...) Expand all
526 target_; 525 target_;
527 526
528 std::map<FilePath, InotifyReader::Watch>::iterator start_it = 527 std::map<FilePath, InotifyReader::Watch>::iterator start_it =
529 recursive_watches_by_path_.lower_bound(changed_dir); 528 recursive_watches_by_path_.lower_bound(changed_dir);
530 std::map<FilePath, InotifyReader::Watch>::iterator end_it = start_it; 529 std::map<FilePath, InotifyReader::Watch>::iterator end_it = start_it;
531 for (; end_it != recursive_watches_by_path_.end(); ++end_it) { 530 for (; end_it != recursive_watches_by_path_.end(); ++end_it) {
532 const FilePath& cur_path = end_it->first; 531 const FilePath& cur_path = end_it->first;
533 if (!changed_dir.IsParent(cur_path)) 532 if (!changed_dir.IsParent(cur_path))
534 break; 533 break;
535 if (!DirectoryExists(cur_path)) 534 if (!DirectoryExists(cur_path))
536 g_inotify_reader.Get().RemoveWatch(end_it->second, this); 535 GetInotifyReader()->RemoveWatch(end_it->second, this);
537 } 536 }
538 recursive_watches_by_path_.erase(start_it, end_it); 537 recursive_watches_by_path_.erase(start_it, end_it);
539 UpdateRecursiveWatchesForPath(changed_dir); 538 UpdateRecursiveWatchesForPath(changed_dir);
540 } 539 }
541 540
542 void FilePathWatcherImpl::UpdateRecursiveWatchesForPath(const FilePath& path) { 541 void FilePathWatcherImpl::UpdateRecursiveWatchesForPath(const FilePath& path) {
543 DCHECK(recursive_); 542 DCHECK(recursive_);
544 DCHECK(!path.empty()); 543 DCHECK(!path.empty());
545 DCHECK(DirectoryExists(path)); 544 DCHECK(DirectoryExists(path));
546 545
547 // Note: SHOW_SYM_LINKS exposes symlinks as symlinks, so they are ignored 546 // Note: SHOW_SYM_LINKS exposes symlinks as symlinks, so they are ignored
548 // rather than followed. Following symlinks can easily lead to the undesirable 547 // rather than followed. Following symlinks can easily lead to the undesirable
549 // situation where the entire file system is being watched. 548 // situation where the entire file system is being watched.
550 FileEnumerator enumerator( 549 FileEnumerator enumerator(
551 path, 550 path,
552 true /* recursive enumeration */, 551 true /* recursive enumeration */,
553 FileEnumerator::DIRECTORIES | FileEnumerator::SHOW_SYM_LINKS); 552 FileEnumerator::DIRECTORIES | FileEnumerator::SHOW_SYM_LINKS);
554 for (FilePath current = enumerator.Next(); 553 for (FilePath current = enumerator.Next();
555 !current.empty(); 554 !current.empty();
556 current = enumerator.Next()) { 555 current = enumerator.Next()) {
557 DCHECK(enumerator.GetInfo().IsDirectory()); 556 DCHECK(enumerator.GetInfo().IsDirectory());
558 557
559 if (!ContainsKey(recursive_watches_by_path_, current)) { 558 if (!ContainsKey(recursive_watches_by_path_, current)) {
560 // Add new watches. 559 // Add new watches.
561 InotifyReader::Watch watch = 560 InotifyReader::Watch watch = GetInotifyReader()->AddWatch(current, this);
562 g_inotify_reader.Get().AddWatch(current, this);
563 TrackWatchForRecursion(watch, current); 561 TrackWatchForRecursion(watch, current);
564 } else { 562 } else {
565 // Update existing watches. 563 // Update existing watches.
566 InotifyReader::Watch old_watch = recursive_watches_by_path_[current]; 564 InotifyReader::Watch old_watch = recursive_watches_by_path_[current];
567 DCHECK_NE(InotifyReader::kInvalidWatch, old_watch); 565 DCHECK_NE(InotifyReader::kInvalidWatch, old_watch);
568 InotifyReader::Watch watch = 566 InotifyReader::Watch watch = GetInotifyReader()->AddWatch(current, this);
569 g_inotify_reader.Get().AddWatch(current, this);
570 if (watch != old_watch) { 567 if (watch != old_watch) {
571 g_inotify_reader.Get().RemoveWatch(old_watch, this); 568 GetInotifyReader()->RemoveWatch(old_watch, this);
572 recursive_paths_by_watch_.erase(old_watch); 569 recursive_paths_by_watch_.erase(old_watch);
573 recursive_watches_by_path_.erase(current); 570 recursive_watches_by_path_.erase(current);
574 TrackWatchForRecursion(watch, current); 571 TrackWatchForRecursion(watch, current);
575 } 572 }
576 } 573 }
577 } 574 }
578 } 575 }
579 576
580 void FilePathWatcherImpl::TrackWatchForRecursion(InotifyReader::Watch watch, 577 void FilePathWatcherImpl::TrackWatchForRecursion(InotifyReader::Watch watch,
581 const FilePath& path) { 578 const FilePath& path) {
(...skipping 11 matching lines...) Expand all
593 } 590 }
594 591
595 void FilePathWatcherImpl::RemoveRecursiveWatches() { 592 void FilePathWatcherImpl::RemoveRecursiveWatches() {
596 if (!recursive_) 593 if (!recursive_)
597 return; 594 return;
598 595
599 for (hash_map<InotifyReader::Watch, FilePath>::const_iterator it = 596 for (hash_map<InotifyReader::Watch, FilePath>::const_iterator it =
600 recursive_paths_by_watch_.begin(); 597 recursive_paths_by_watch_.begin();
601 it != recursive_paths_by_watch_.end(); 598 it != recursive_paths_by_watch_.end();
602 ++it) { 599 ++it) {
603 g_inotify_reader.Get().RemoveWatch(it->first, this); 600 GetInotifyReader()->RemoveWatch(it->first, this);
604 } 601 }
605 recursive_paths_by_watch_.clear(); 602 recursive_paths_by_watch_.clear();
606 recursive_watches_by_path_.clear(); 603 recursive_watches_by_path_.clear();
607 } 604 }
608 605
609 void FilePathWatcherImpl::AddWatchForBrokenSymlink(const FilePath& path, 606 void FilePathWatcherImpl::AddWatchForBrokenSymlink(const FilePath& path,
610 WatchEntry* watch_entry) { 607 WatchEntry* watch_entry) {
611 DCHECK_EQ(InotifyReader::kInvalidWatch, watch_entry->watch); 608 DCHECK_EQ(InotifyReader::kInvalidWatch, watch_entry->watch);
612 FilePath link; 609 FilePath link;
613 if (!ReadSymbolicLink(path, &link)) 610 if (!ReadSymbolicLink(path, &link))
614 return; 611 return;
615 612
616 if (!link.IsAbsolute()) 613 if (!link.IsAbsolute())
617 link = path.DirName().Append(link); 614 link = path.DirName().Append(link);
618 615
619 // Try watching symlink target directory. If the link target is "/", then we 616 // Try watching symlink target directory. If the link target is "/", then we
620 // shouldn't get here in normal situations and if we do, we'd watch "/" for 617 // shouldn't get here in normal situations and if we do, we'd watch "/" for
621 // changes to a component "/" which is harmless so no special treatment of 618 // changes to a component "/" which is harmless so no special treatment of
622 // this case is required. 619 // this case is required.
623 InotifyReader::Watch watch = 620 InotifyReader::Watch watch =
624 g_inotify_reader.Get().AddWatch(link.DirName(), this); 621 GetInotifyReader()->AddWatch(link.DirName(), this);
625 if (watch == InotifyReader::kInvalidWatch) { 622 if (watch == InotifyReader::kInvalidWatch) {
626 // TODO(craig) Symlinks only work if the parent directory for the target 623 // TODO(craig) Symlinks only work if the parent directory for the target
627 // exist. Ideally we should make sure we've watched all the components of 624 // exist. Ideally we should make sure we've watched all the components of
628 // the symlink path for changes. See crbug.com/91561 for details. 625 // the symlink path for changes. See crbug.com/91561 for details.
629 DPLOG(WARNING) << "Watch failed for " << link.DirName().value(); 626 DPLOG(WARNING) << "Watch failed for " << link.DirName().value();
630 return; 627 return;
631 } 628 }
632 watch_entry->watch = watch; 629 watch_entry->watch = watch;
633 watch_entry->linkname = link.BaseName().value(); 630 watch_entry->linkname = link.BaseName().value();
634 } 631 }
635 632
636 bool FilePathWatcherImpl::HasValidWatchVector() const { 633 bool FilePathWatcherImpl::HasValidWatchVector() const {
637 if (watches_.empty()) 634 if (watches_.empty())
638 return false; 635 return false;
639 for (size_t i = 0; i < watches_.size() - 1; ++i) { 636 for (size_t i = 0; i < watches_.size() - 1; ++i) {
640 if (watches_[i].subdir.empty()) 637 if (watches_[i].subdir.empty())
641 return false; 638 return false;
642 } 639 }
643 return watches_.back().subdir.empty(); 640 return watches_.back().subdir.empty();
644 } 641 }
645 642
646 } // namespace 643 } // namespace
647 644
648 FilePathWatcher::FilePathWatcher() { 645 FilePathWatcher::FilePathWatcher() {
649 sequence_checker_.DetachFromSequence(); 646 sequence_checker_.DetachFromSequence();
650 impl_ = MakeUnique<FilePathWatcherImpl>(); 647 impl_ = MakeUnique<FilePathWatcherImpl>();
651 } 648 }
652 649
653 } // namespace base 650 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698