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

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

Issue 1491743009: [base] POSIX File::Unlock() didn't actually unlock file. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: forgot some comments Created 5 years 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
« no previous file with comments | « base/files/file_posix.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/command_line.h"
5 #include "base/files/file.h" 6 #include "base/files/file.h"
6 #include "base/files/file_util.h" 7 #include "base/files/file_util.h"
7 #include "base/files/scoped_temp_dir.h" 8 #include "base/files/scoped_temp_dir.h"
9 #include "base/test/multiprocess_test.h"
10 #include "base/test/test_timeouts.h"
11 #include "base/threading/platform_thread.h"
8 #include "base/time/time.h" 12 #include "base/time/time.h"
9 #include "testing/gtest/include/gtest/gtest.h" 13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "testing/multiprocess_func_list.h"
10 15
11 using base::File; 16 using base::File;
12 using base::FilePath; 17 using base::FilePath;
13 18
14 TEST(FileTest, Create) { 19 TEST(FileTest, Create) {
15 base::ScopedTempDir temp_dir; 20 base::ScopedTempDir temp_dir;
16 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 21 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
17 FilePath file_path = temp_dir.path().AppendASCII("create_file_1"); 22 FilePath file_path = temp_dir.path().AppendASCII("create_file_1");
18 23
19 { 24 {
(...skipping 482 matching lines...) Expand 10 before | Expand all | Expand 10 after
502 NULL)); 507 NULL));
503 ASSERT_TRUE(dir.IsValid()); 508 ASSERT_TRUE(dir.IsValid());
504 509
505 base::File::Info info; 510 base::File::Info info;
506 EXPECT_TRUE(dir.GetInfo(&info)); 511 EXPECT_TRUE(dir.GetInfo(&info));
507 EXPECT_TRUE(info.is_directory); 512 EXPECT_TRUE(info.is_directory);
508 EXPECT_FALSE(info.is_symbolic_link); 513 EXPECT_FALSE(info.is_symbolic_link);
509 EXPECT_EQ(0, info.size); 514 EXPECT_EQ(0, info.size);
510 } 515 }
511 #endif // defined(OS_WIN) 516 #endif // defined(OS_WIN)
517
518 // Constants and helper function for lock/unlock tests.
519 namespace {
520
521 // Flag for the parent to share a temp dir to the child.
522 const char kTempDirFlag[] = "temp-dir";
523
524 // File to lock in temp dir.
525 const char kLockFile[] = "lockfile";
526
527 // Constants for various requests and responses, used as |signal_file| parameter
528 // to signal/wait helpers.
529 const char kSignalLockFileLock[] = "lock.signal";
530 const char kSignalLockFileLocked[] = "locked.signal";
531 const char kSignalLockFileClose[] = "close.signal";
532 const char kSignalLockFileClosed[] = "closed.signal";
533 const char kSignalLockFileUnlock[] = "unlock.signal";
534 const char kSignalLockFileUnlocked[] = "unlocked.signal";
535 const char kSignalExit[] = "exit.signal";
536
537 // Signal an event by creating a file which didn't previously exist.
538 bool SignalEvent(const base::FilePath& signal_dir,
539 const base::StringPiece& signal_file) {
540 File file(signal_dir.AppendASCII(signal_file),
541 File::FLAG_CREATE | File::FLAG_APPEND);
542 return file.IsValid();
543 }
544
545 // Check whether an event was signaled.
546 bool CheckEvent(const base::FilePath& signal_dir,
547 const base::StringPiece& signal_file) {
548 File file(signal_dir.AppendASCII(signal_file),
549 File::FLAG_OPEN | File::FLAG_READ);
550 return file.IsValid();
551 }
552
553 // Wait for an event to be signaled, returning false if it times out.
554 bool WaitForEventWithTimeout(const base::FilePath& signal_dir,
555 const base::StringPiece& signal_file,
556 const base::TimeDelta& timeout) {
557 const base::Time finish_by = base::Time::Now() + timeout;
558 while (!CheckEvent(signal_dir, signal_file)) {
559 if (base::Time::Now() > finish_by)
560 return false;
561 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
562 }
563 return true;
564 }
565
566 // Wait forever for the event to be signaled (should never return false).
567 bool WaitForEvent(const base::FilePath& signal_dir,
568 const base::StringPiece& signal_file) {
569 return WaitForEventWithTimeout(signal_dir, signal_file,
570 base::TimeDelta::Max());
571 }
572
573 // Wait for an event or the action timeout, whichever comes first.
574 bool WaitForEventOrTimeout(const base::FilePath& signal_dir,
575 const base::StringPiece& signal_file) {
576 return WaitForEventWithTimeout(signal_dir, signal_file,
577 TestTimeouts::action_timeout());
578 }
579
580 } // namespace
581
582 // Subprocess to test getting a file lock then releasing it.
583 MULTIPROCESS_TEST_MAIN(ChildLockUnlock) {
584 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
585 const base::FilePath temp_path =
586 command_line->GetSwitchValuePath(kTempDirFlag);
587 CHECK(base::DirectoryExists(temp_path));
588
589 // Wait for signal to lock, then lock the file.
590 CHECK(WaitForEvent(temp_path, kSignalLockFileLock));
591 File file(temp_path.AppendASCII(kLockFile),
592 File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE);
593 CHECK(file.IsValid());
594 CHECK_EQ(File::FILE_OK, file.Lock());
595 CHECK(SignalEvent(temp_path, kSignalLockFileLocked));
596
597 // Wait for signal to unlock, then unlock the file.
598 CHECK(WaitForEvent(temp_path, kSignalLockFileUnlock));
599 CHECK_EQ(File::FILE_OK, file.Unlock());
600 CHECK(SignalEvent(temp_path, kSignalLockFileUnlocked));
601
602 // Wait for signal to exit, so that the unlock can be distinguished from exit.
603 CHECK(WaitForEvent(temp_path, kSignalExit));
604 return 0;
605 }
606
607 // Test that the child's lock prevents the parent from getting the lock, and
608 // that the child's unlock allows the parent to get the lock.
609 TEST(FileTest, LockAndUnlock) {
610 // Create a temporary dir and spin up a ChildLockExit subprocess against it.
611 base::ScopedTempDir temp_dir;
612 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
613 const base::FilePath temp_path = temp_dir.path();
614 base::CommandLine child_command_line(
615 base::GetMultiProcessTestChildBaseCommandLine());
616 child_command_line.AppendSwitchPath(kTempDirFlag, temp_path);
617 base::Process test_child_process =
Lei Zhang 2015/12/04 03:23:03 Check |test_child_process| is valid before continu
618 base::SpawnMultiProcessTestChild("ChildLockUnlock", child_command_line,
619 base::LaunchOptions());
620
621 // Create the lock file and verify that it can be locked and unlocked.
622 File file(temp_path.AppendASCII(kLockFile),
623 File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE);
624 ASSERT_TRUE(file.IsValid());
625 ASSERT_EQ(File::FILE_OK, file.Lock());
626 ASSERT_EQ(File::FILE_OK, file.Unlock());
627
628 // Signal child to lock the file.
629 ASSERT_TRUE(SignalEvent(temp_path, kSignalLockFileLock));
630
631 // After child has locked the file, test the lock, then signal child to unlock
632 // the file.
633 ASSERT_TRUE(WaitForEventOrTimeout(temp_path, kSignalLockFileLocked));
634 ASSERT_NE(File::FILE_OK, file.Lock());
635 ASSERT_TRUE(SignalEvent(temp_path, kSignalLockFileUnlock));
636
637 // After child has unlocked, test that the file can be locked and signal the
638 // child to exit.
639 ASSERT_TRUE(WaitForEventOrTimeout(temp_path, kSignalLockFileUnlocked));
640 ASSERT_EQ(File::FILE_OK, file.Lock());
641 ASSERT_EQ(File::FILE_OK, file.Unlock());
642 ASSERT_TRUE(SignalEvent(temp_path, kSignalExit));
643
644 int rv = -1;
645 ASSERT_TRUE(test_child_process.WaitForExitWithTimeout(
646 TestTimeouts::action_timeout(), &rv));
647 ASSERT_EQ(0, rv);
648 }
649
650 // Subprocess to test getting a lock then releasing it implicitly on close.
651 MULTIPROCESS_TEST_MAIN(ChildLockClose) {
652 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
653 const base::FilePath temp_path =
654 command_line->GetSwitchValuePath(kTempDirFlag);
655 CHECK(base::DirectoryExists(temp_path));
656
657 // Wait for signal to lock, then lock the file.
658 CHECK(WaitForEvent(temp_path, kSignalLockFileLock));
659 File file(temp_path.AppendASCII(kLockFile),
660 File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE);
661 CHECK(file.IsValid());
662 CHECK_EQ(File::FILE_OK, file.Lock());
663 CHECK(SignalEvent(temp_path, kSignalLockFileLocked));
664
665 // Wait for the signal to close, then close the file.
666 CHECK(WaitForEvent(temp_path, kSignalLockFileClose));
Lei Zhang 2015/12/04 03:23:03 Would it easier to have one child process impl tha
Scott Hess - ex-Googler 2015/12/04 06:40:34 Just to clarify, you mean to merge the different c
Lei Zhang 2015/12/04 16:57:54 If it's easy to implement and it reduces the amoun
667 file.Close();
668 CHECK(SignalEvent(temp_path, kSignalLockFileClosed));
669
670 // Wait for signal to exit, so that close can be distinguished from exit.
671 CHECK(WaitForEvent(temp_path, kSignalExit));
672 return 0;
673 }
674
675 // Test that closing the handle in the child implicitly releases the lock.
676 TEST(FileTest, UnlockOnClose) {
677 // Create a temporary dir and spin up a ChildLockExit subprocess against it.
678 base::ScopedTempDir temp_dir;
679 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
680 const base::FilePath temp_path = temp_dir.path();
681 base::CommandLine child_command_line(
682 base::GetMultiProcessTestChildBaseCommandLine());
683 child_command_line.AppendSwitchPath(kTempDirFlag, temp_path);
684 base::Process test_child_process =
685 base::SpawnMultiProcessTestChild("ChildLockClose", child_command_line,
686 base::LaunchOptions());
687
688 // Create the lock file and verify that it can be locked and unlocked.
689 File file(temp_path.AppendASCII(kLockFile),
690 File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE);
691 ASSERT_TRUE(file.IsValid());
692 ASSERT_EQ(File::FILE_OK, file.Lock());
693 ASSERT_EQ(File::FILE_OK, file.Unlock());
694
695 // Signal child to lock the file.
696 ASSERT_TRUE(SignalEvent(temp_path, kSignalLockFileLock));
697
698 // After child has locked the file, test the lock, then signal child to close
699 // the file.
700 ASSERT_TRUE(WaitForEventOrTimeout(temp_path, kSignalLockFileLocked));
701 ASSERT_NE(File::FILE_OK, file.Lock());
702 ASSERT_TRUE(SignalEvent(temp_path, kSignalLockFileClose));
703
704 // After child has closed, test that the file can be locked and signal the
705 // child to exit.
706 ASSERT_TRUE(WaitForEventOrTimeout(temp_path, kSignalLockFileClosed));
707 ASSERT_EQ(File::FILE_OK, file.Lock());
708 ASSERT_EQ(File::FILE_OK, file.Unlock());
709 ASSERT_TRUE(SignalEvent(temp_path, kSignalExit));
710
711 int rv = -1;
712 ASSERT_TRUE(test_child_process.WaitForExitWithTimeout(
713 TestTimeouts::action_timeout(), &rv));
714 ASSERT_EQ(0, rv);
715 }
716
717 // Subprocess to test getting a lock then releasing it implicitly on exit or
718 // termination.
719 MULTIPROCESS_TEST_MAIN(ChildLockExit) {
720 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
721 const base::FilePath temp_path =
722 command_line->GetSwitchValuePath(kTempDirFlag);
723 CHECK(base::DirectoryExists(temp_path));
724
725 // Wait for signal to lock, then lock the file.
726 CHECK(WaitForEvent(temp_path, kSignalLockFileLock));
727 File file(temp_path.AppendASCII(kLockFile),
728 File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE);
729 CHECK(file.IsValid());
730 CHECK_EQ(File::FILE_OK, file.Lock());
731 CHECK(SignalEvent(temp_path, kSignalLockFileLocked));
732
733 // Wait for signal to exit, so that exit can be distinguished from kill.
734 CHECK(WaitForEvent(temp_path, kSignalExit));
735 return 0;
736 }
737
738 // Test that locks are released on exit.
739 TEST(FileTest, UnlockOnExit) {
740 // Create a temporary dir and spin up a ChildLockExit subprocess against it.
741 base::ScopedTempDir temp_dir;
742 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
743 const base::FilePath temp_path = temp_dir.path();
744 base::CommandLine child_command_line(
745 base::GetMultiProcessTestChildBaseCommandLine());
746 child_command_line.AppendSwitchPath(kTempDirFlag, temp_path);
747 base::Process test_child_process =
748 base::SpawnMultiProcessTestChild("ChildLockExit", child_command_line,
749 base::LaunchOptions());
750
751 // Create the lock file and verify that it can be locked and unlocked.
752 File file(temp_path.AppendASCII(kLockFile),
753 File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE);
754 ASSERT_TRUE(file.IsValid());
755 ASSERT_EQ(File::FILE_OK, file.Lock());
756 ASSERT_EQ(File::FILE_OK, file.Unlock());
757
758 // Signal child to lock the file.
759 ASSERT_TRUE(SignalEvent(temp_path, kSignalLockFileLock));
760
761 // After child has locked the file, test the lock, then signal child to exit.
762 ASSERT_TRUE(WaitForEventOrTimeout(temp_path, kSignalLockFileLocked));
763 ASSERT_NE(File::FILE_OK, file.Lock());
764 ASSERT_TRUE(SignalEvent(temp_path, kSignalExit));
765
766 int rv = -1;
767 ASSERT_TRUE(test_child_process.WaitForExitWithTimeout(
768 TestTimeouts::action_timeout(), &rv));
769 ASSERT_EQ(0, rv);
770
771 // Exit released the child's lock.
772 ASSERT_EQ(File::FILE_OK, file.Lock());
773 ASSERT_EQ(File::FILE_OK, file.Unlock());
774 }
775
776 // Test that locks are released when a process is terminated. This should cover
777 // the case of crashing, also.
778 TEST(FileTest, UnlockOnTerminate) {
779 // Create a temporary dir and spin up a ChildLockExit subprocess against it.
780 base::ScopedTempDir temp_dir;
781 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
782 const base::FilePath temp_path = temp_dir.path();
783 base::CommandLine child_command_line(
784 base::GetMultiProcessTestChildBaseCommandLine());
785 child_command_line.AppendSwitchPath(kTempDirFlag, temp_path);
786 base::Process test_child_process =
787 base::SpawnMultiProcessTestChild("ChildLockExit", child_command_line,
788 base::LaunchOptions());
789
790 // Create the lock file and verify that it can be locked and unlocked.
791 File file(temp_path.AppendASCII(kLockFile),
792 File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE);
793 ASSERT_TRUE(file.IsValid());
794 ASSERT_EQ(File::FILE_OK, file.Lock());
795 ASSERT_EQ(File::FILE_OK, file.Unlock());
796
797 // Signal child to lock the file.
798 ASSERT_TRUE(SignalEvent(temp_path, kSignalLockFileLock));
799
800 // After child has locked the file, test the lock, then terminate the child.
801 ASSERT_TRUE(WaitForEventOrTimeout(temp_path, kSignalLockFileLocked));
802 ASSERT_NE(File::FILE_OK, file.Lock());
803 ASSERT_TRUE(test_child_process.Terminate(0, true));
804
805 // Termination released the child's lock.
806 ASSERT_EQ(File::FILE_OK, file.Lock());
807 ASSERT_EQ(File::FILE_OK, file.Unlock());
808 }
OLDNEW
« no previous file with comments | « base/files/file_posix.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698