Introduce InputFile/OutputFile and FileMutex.
FileHandle is replaced with InputFile/OutputFile and FileMutex.
Use InputFile when you want to open a file in read-only.
USe OutputFile when you open a file for writing.
Both of them provide a reliable way to access the files and perform
the I/O operations.
Given a name "foo", FileMutex creates a file named "foo.lock" and
tries to acquire an advisory lock (flock) on this file.
FileHandle, which uses the file it's openning for locking, may corrupt
the file contents when two or more processes are trying to gain the
lock for reading/writing. For example:
Process #2 creates foo
Process #1 opens foo
Process #2 opens foo
Process #2 locks foo (exclusively) (success)
Process #1 locks foo (failed, retry #1)
Process #2 starts writing foo
Process #1 opens and truncates foo (note there’s O_TRUNC in the flag)
Process #2 writes foo continually (foo is corrupted from now on ...)
Process #1 locks foo (failed, retry #2)
...
Process #1 locks foo (reach the max retries and return)
Process #2 gets done on writing foo (foo is corrupted ...)
Process #2 unlocks and closes foo (foo is corrupted)
diff --git a/lib/ExecutionEngine/OutputFile.cpp b/lib/ExecutionEngine/OutputFile.cpp
new file mode 100644
index 0000000..167b7a0
--- /dev/null
+++ b/lib/ExecutionEngine/OutputFile.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2012, 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 "OutputFile.h"
+
+#include <cstdlib>
+
+#include <llvm/Support/raw_ostream.h>
+
+#include "DebugHelper.h"
+
+using namespace bcc;
+
+OutputFile *OutputFile::CreateTemporary(const std::string &pFileTemplate,
+ unsigned pFlags) {
+ char *tmp_filename = NULL;
+ int tmp_fd;
+ OutputFile *result = NULL;
+
+ // Allocate memory to hold the generated unique temporary filename.
+ tmp_filename =
+ new (std::nothrow) char [ pFileTemplate.length() + /* .XXXXXX */7 + 1 ];
+ if (tmp_filename == NULL) {
+ ALOGE("Out of memory when allocates memory for filename %s in "
+ "OutputFile::CreateTemporary()!", pFileTemplate.c_str());
+ return NULL;
+ }
+
+ // Construct filename template for mkstemp().
+ if (pFileTemplate.length() > 0)
+ ::memcpy(tmp_filename, pFileTemplate.c_str(), pFileTemplate.length());
+ ::strncpy(tmp_filename + pFileTemplate.length(), ".XXXXXX", 7);
+
+ // POSIX mkstemp() never returns EINTR.
+ tmp_fd = ::mkstemp(tmp_filename);
+ if (tmp_fd < 0) {
+ llvm::error_code err(errno, llvm::posix_category());
+ ALOGE("Failed to create temporary file using mkstemp() for %s! (%s)",
+ tmp_filename, err.message().c_str());
+ delete [] tmp_filename;
+ return NULL;
+ }
+
+ // Create result OutputFile.
+ result = new (std::nothrow) OutputFile(tmp_filename, pFlags);
+ if (result == NULL) {
+ ALOGE("Out of memory when creates OutputFile for %s!", tmp_filename);
+ // Fall through to the clean-up codes.
+ } else {
+ if (result->hasError()) {
+ ALOGE("Failed to open temporary output file %s! (%s)",
+ result->getName().c_str(), result->getErrorMessage().c_str());
+ delete result;
+ result = NULL;
+ // Fall through to the clean-up codes.
+ }
+ }
+
+ // Clean up.
+ delete [] tmp_filename;
+ ::close(tmp_fd);
+
+ return result;
+}
+
+OutputFile::OutputFile(const std::string &pFilename, unsigned pFlags)
+ : super(pFilename, pFlags) { }
+
+ssize_t OutputFile::write(const void *pBuf, size_t count) {
+ if ((mFD < 0) || hasError()) {
+ return -1;
+ }
+
+ if ((count <= 0) || (pBuf == NULL)) {
+ // Keep safe and issue a warning.
+ ALOGW("OutputFile::write: count = %u, buffer = %p", count, pBuf);
+ return 0;
+ }
+
+ while (count > 0) {
+ ssize_t write_size = ::write(mFD, pBuf, count);
+
+ if (write_size > 0) {
+ return write_size;
+ } else if ((errno == EAGAIN) || (errno == EINTR)) {
+ // If the errno is EAGAIN or EINTR, then we try to write again.
+ //
+ // Fall-through
+ } else {
+ detectError();
+ return -1;
+ }
+ }
+ // unreachable
+ return 0;
+}
+
+void OutputFile::truncate() {
+ if (mFD < 0) {
+ return;
+ }
+
+ do {
+ if (::ftruncate(mFD, 0) == 0) {
+ return;
+ }
+ } while (errno == EINTR);
+ detectError();
+
+ return;
+}
+
+llvm::raw_fd_ostream *OutputFile::dup() {
+ int newfd;
+
+ do {
+ newfd = ::dup(mFD);
+ if (newfd < 0) {
+ if (errno != EINTR) {
+ detectError();
+ return NULL;
+ }
+ // EINTR
+ continue;
+ }
+ // dup() returns ok.
+ break;
+ } while (true);
+
+ llvm::raw_fd_ostream *result =
+ new (std::nothrow) llvm::raw_fd_ostream(newfd, /* shouldClose */true);
+
+ if (result == NULL) {
+ mError.assign(llvm::errc::not_enough_memory, llvm::system_category());
+ }
+
+ return result;
+}