Merge "Add locking around boot image generation."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index b32fc9b..e305dc8 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -25,6 +25,7 @@
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 7a832c1..8d532c7 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -26,6 +26,7 @@
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 0000000..c0bce84
--- /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 0000000..26b4eb0
--- /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 0000000..8fa181a
--- /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 330b110..28164cd 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 @@
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 @@
// 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 3d35c00..61633cd 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 @@
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) {