diff options
| -rw-r--r-- | build/Android.gtest.mk | 1 | ||||
| -rw-r--r-- | runtime/Android.mk | 1 | ||||
| -rw-r--r-- | runtime/base/scoped_flock.cc | 75 | ||||
| -rw-r--r-- | runtime/base/scoped_flock.h | 51 | ||||
| -rw-r--r-- | runtime/base/scoped_flock_test.cc | 41 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 57 | ||||
| -rw-r--r-- | runtime/gc/space/image_space.cc | 13 |
7 files changed, 183 insertions, 56 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index b32fc9b78e..e305dc8c53 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -25,6 +25,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \ runtime/base/hex_dump_test.cc \ runtime/base/histogram_test.cc \ runtime/base/mutex_test.cc \ + runtime/base/scoped_flock_test.cc \ runtime/base/timing_logger_test.cc \ runtime/base/unix_file/fd_file_test.cc \ runtime/base/unix_file/mapped_file_test.cc \ diff --git a/runtime/Android.mk b/runtime/Android.mk index 7a832c157a..8d532c767e 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -26,6 +26,7 @@ LIBART_COMMON_SRC_FILES := \ base/hex_dump.cc \ base/logging.cc \ base/mutex.cc \ + base/scoped_flock.cc \ base/stringpiece.cc \ base/stringprintf.cc \ base/timing_logger.cc \ diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc new file mode 100644 index 0000000000..c0bce840e4 --- /dev/null +++ b/runtime/base/scoped_flock.cc @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "scoped_flock.h" + +#include <sys/file.h> +#include <sys/stat.h> + +#include "base/logging.h" +#include "base/stringprintf.h" +#include "base/unix_file/fd_file.h" + +namespace art { + +bool ScopedFlock::Init(const char* filename, std::string* error_msg) { + while (true) { + file_.reset(OS::OpenFileWithFlags(filename, O_CREAT | O_RDWR)); + if (file_.get() == NULL) { + *error_msg = StringPrintf("Failed to open file '%s': %s", filename, strerror(errno)); + return false; + } + int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_EX)); + if (flock_result != 0) { + *error_msg = StringPrintf("Failed to lock file '%s': %s", filename, strerror(errno)); + return false; + } + struct stat fstat_stat; + int fstat_result = TEMP_FAILURE_RETRY(fstat(file_->Fd(), &fstat_stat)); + if (fstat_result != 0) { + *error_msg = StringPrintf("Failed to fstat file '%s': %s", filename, strerror(errno)); + return false; + } + struct stat stat_stat; + int stat_result = TEMP_FAILURE_RETRY(stat(filename, &stat_stat)); + if (stat_result != 0) { + PLOG(WARNING) << "Failed to stat, will retry: " << filename; + // ENOENT can happen if someone racing with us unlinks the file we created so just retry. + continue; + } + if (fstat_stat.st_dev != stat_stat.st_dev || fstat_stat.st_ino != stat_stat.st_ino) { + LOG(WARNING) << "File changed while locking, will retry: " << filename; + continue; + } + return true; + } +} + +File* ScopedFlock::GetFile() { + CHECK(file_.get() != NULL); + return file_.get(); +} + +ScopedFlock::ScopedFlock() { } + +ScopedFlock::~ScopedFlock() { + if (file_.get() != NULL) { + int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN)); + CHECK_EQ(0, flock_result); + } +} + +} // namespace art diff --git a/runtime/base/scoped_flock.h b/runtime/base/scoped_flock.h new file mode 100644 index 0000000000..26b4eb0c2e --- /dev/null +++ b/runtime/base/scoped_flock.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_BASE_SCOPED_FLOCK_H_ +#define ART_RUNTIME_BASE_SCOPED_FLOCK_H_ + +#include <memory> +#include <string> + +#include "base/macros.h" +#include "os.h" + +namespace art { + +class ScopedFlock { + public: + ScopedFlock(); + + // Attempts to acquire an exclusive file lock (see flock(2)) on the file + // at filename, and blocks until it can do so. + // + // Returns true if the lock could be acquired, or false if an error + // occurred. It is an error if the file does not exist, or if its inode + // changed (usually due to a new file being created at the same path) + // between attempts to lock it. + bool Init(const char* filename, std::string* error_msg); + + // Returns the (locked) file associated with this instance. + File* GetFile(); + ~ScopedFlock(); + private: + std::unique_ptr<File> file_; + DISALLOW_COPY_AND_ASSIGN(ScopedFlock); +}; + +} // namespace art + +#endif // ART_RUNTIME_BASE_SCOPED_FLOCK_H_ diff --git a/runtime/base/scoped_flock_test.cc b/runtime/base/scoped_flock_test.cc new file mode 100644 index 0000000000..8fa181ab6b --- /dev/null +++ b/runtime/base/scoped_flock_test.cc @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "scoped_flock.h" +#include "common_runtime_test.h" + +#include "gtest/gtest.h" + +namespace art { + +class ScopedFlockTest : public CommonRuntimeTest {}; + +TEST_F(ScopedFlockTest, TestLocking) { + ScratchFile scratch_file; + std::string error_msg; + + // NOTE: Locks applied using flock(2) and fcntl(2) are oblivious + // to each other, so attempting to query locks set by flock using + // using fcntl(,F_GETLK,) will not work. see kernel doc at + // Documentation/filesystems/locks.txt. + ScopedFlock file_lock; + ASSERT_TRUE(file_lock.Init(scratch_file.GetFilename().c_str(), + &error_msg)); + + ASSERT_FALSE(file_lock.Init("/guaranteed/not/to/exist", &error_msg)); +} + +} // namespace art diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 330b110e90..28164cd784 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -27,6 +27,7 @@ #include "base/casts.h" #include "base/logging.h" +#include "base/scoped_flock.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" @@ -701,60 +702,6 @@ const DexFile* ClassLinker::FindDexFileInOatLocation(const char* dex_location, return dex_file; } -class ScopedFlock { - public: - ScopedFlock() {} - - bool Init(const char* filename, std::string* error_msg) { - while (true) { - file_.reset(OS::OpenFileWithFlags(filename, O_CREAT | O_RDWR)); - if (file_.get() == NULL) { - *error_msg = StringPrintf("Failed to open file '%s': %s", filename, strerror(errno)); - return false; - } - int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_EX)); - if (flock_result != 0) { - *error_msg = StringPrintf("Failed to lock file '%s': %s", filename, strerror(errno)); - return false; - } - struct stat fstat_stat; - int fstat_result = TEMP_FAILURE_RETRY(fstat(file_->Fd(), &fstat_stat)); - if (fstat_result != 0) { - *error_msg = StringPrintf("Failed to fstat file '%s': %s", filename, strerror(errno)); - return false; - } - struct stat stat_stat; - int stat_result = TEMP_FAILURE_RETRY(stat(filename, &stat_stat)); - if (stat_result != 0) { - PLOG(WARNING) << "Failed to stat, will retry: " << filename; - // ENOENT can happen if someone racing with us unlinks the file we created so just retry. - continue; - } - if (fstat_stat.st_dev != stat_stat.st_dev || fstat_stat.st_ino != stat_stat.st_ino) { - LOG(WARNING) << "File changed while locking, will retry: " << filename; - continue; - } - return true; - } - } - - File& GetFile() { - return *file_; - } - - ~ScopedFlock() { - if (file_.get() != NULL) { - int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN)); - CHECK_EQ(0, flock_result); - } - } - - private: - std::unique_ptr<File> file_; - - DISALLOW_COPY_AND_ASSIGN(ScopedFlock); -}; - const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation( const char* dex_location, uint32_t dex_location_checksum, @@ -785,7 +732,7 @@ const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation( // Generate the output oat file for the dex file VLOG(class_linker) << "Generating oat file " << oat_location << " for " << dex_location; - if (!GenerateOatFile(dex_location, scoped_flock.GetFile().Fd(), oat_location, &error_msg)) { + if (!GenerateOatFile(dex_location, scoped_flock.GetFile()->Fd(), oat_location, &error_msg)) { CHECK(!error_msg.empty()); error_msgs->push_back(error_msg); return nullptr; diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 3d35c00e60..61633cd489 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -18,6 +18,7 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" +#include "base/scoped_flock.h" #include "gc/accounting/space_bitmap-inl.h" #include "mirror/art_method.h" #include "mirror/class-inl.h" @@ -148,7 +149,17 @@ ImageSpace* ImageSpace::Create(const char* image_location, std::string image_filename; std::string error_msg; bool is_system = false; - if (FindImageFilename(image_location, image_isa, &image_filename, &is_system)) { + const bool found_image = FindImageFilename(image_location, image_isa, &image_filename, + &is_system); + + // Note that we must not use the file descriptor associated with + // ScopedFlock::GetFile to Init the image file. We want the file + // descriptor (and the associated exclusive lock) to be released when + // we leave Create. + ScopedFlock image_lock; + image_lock.Init(image_filename.c_str(), &error_msg); + + if (found_image) { ImageSpace* space = ImageSpace::Init(image_filename.c_str(), image_location, !is_system, &error_msg); if (space != nullptr) { |