folly/{experimental => .}/File
authorTom Jackson <tjackson@fb.com>
Thu, 21 Feb 2013 00:50:50 +0000 (16:50 -0800)
committerJordan DeLong <jdelong@fb.com>
Tue, 19 Mar 2013 00:07:44 +0000 (17:07 -0700)
Summary: Moving File into `/folly`.

Test Plan: Same unit tests, rebuild affected code outside folly.

Reviewed By: philipp@fb.com

FB internal diff: D714462

folly/File.cpp [new file with mode: 0644]
folly/File.h [new file with mode: 0644]
folly/experimental/FileGen.h
folly/test/FileTest.cpp [new file with mode: 0644]

diff --git a/folly/File.cpp b/folly/File.cpp
new file mode 100644 (file)
index 0000000..b2f8900
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2013 Facebook, Inc.
+ *
+ * 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 "folly/File.h"
+#include "folly/ScopeGuard.h"
+
+#include <system_error>
+
+#include <glog/logging.h>
+
+namespace folly {
+
+File::File()
+  : fd_(-1)
+  , ownsFd_(false)
+{}
+
+File::File(int fd, bool ownsFd)
+  : fd_(fd)
+  , ownsFd_(ownsFd)
+{}
+
+File::File(const char* name, int flags, mode_t mode)
+  : fd_(::open(name, flags, mode))
+  , ownsFd_(false) {
+
+  if (fd_ < 0) {
+    throw std::system_error(errno, std::system_category(), "open() failed");
+  }
+  ownsFd_ = true;
+}
+
+File::File(File&& other)
+  : fd_(other.fd_)
+  , ownsFd_(other.ownsFd_) {
+
+  other.release();
+}
+
+File& File::operator=(File&& other) {
+  closeNoThrow();
+  swap(other);
+  return *this;
+}
+
+File::~File() {
+  closeNoThrow();  // ignore error
+}
+
+/* static */ File File::temporary() {
+  // make a temp file with tmpfile(), dup the fd, then return it in a File.
+  FILE* tmpFile = tmpfile();
+  if (!tmpFile) {
+    throw std::system_error(errno, std::system_category(), "tmpfile() failed");
+  }
+  SCOPE_EXIT { fclose(tmpFile); };
+
+  int fd = dup(fileno(tmpFile));
+  if (fd < 0) {
+    throw std::system_error(errno, std::system_category(), "dup() failed");
+  }
+
+  return File(fd, true);
+}
+
+void File::release() {
+  fd_ = -1;
+  ownsFd_ = false;
+}
+
+void File::swap(File& other) {
+  using std::swap;
+  swap(fd_, other.fd_);
+  swap(ownsFd_, other.ownsFd_);
+}
+
+void swap(File& a, File& b) {
+  a.swap(b);
+}
+
+void File::close() {
+  if (!closeNoThrow()) {
+    throw std::system_error(errno, std::system_category(), "close() failed");
+  }
+}
+
+bool File::closeNoThrow() {
+  int r = ownsFd_ ? ::close(fd_) : 0;
+  release();
+  return r == 0;
+}
+
+}  // namespace folly
diff --git a/folly/File.h b/folly/File.h
new file mode 100644 (file)
index 0000000..1f30c98
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2013 Facebook, Inc.
+ *
+ * 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 FOLLY_FILE_H_
+#define FOLLY_FILE_H_
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+namespace folly {
+
+/**
+ * A File represents an open file.
+ */
+class File {
+ public:
+  /**
+   * Creates an empty File object, for late initialization.
+   */
+  File();
+
+  /**
+   * Create a File object from an existing file descriptor.
+   * Takes ownership of the file descriptor if ownsFd is true.
+   */
+  /* implicit */ File(int fd,
+                      bool ownsFd = false);
+
+  /**
+   * Open and create a file object.  Throws on error.
+   */
+  /* implicit */ File(const char* name,
+                      int flags = O_RDONLY,
+                      mode_t mode = 0644);
+
+  ~File();
+
+  /**
+   * Create and return a temporary, owned file (uses tmpfile()).
+   */
+  static File temporary();
+
+  /**
+   * Return the file descriptor, or -1 if the file was closed.
+   */
+  int fd() const { return fd_; }
+
+  /**
+   * If we own the file descriptor, close the file and throw on error.
+   * Otherwise, do nothing.
+   */
+  void close();
+
+  /**
+   * Closes the file (if owned).  Returns true on success, false (and sets
+   * errno) on error.
+   */
+  bool closeNoThrow();
+
+  /**
+   * Releases the file descriptor; no longer owned by this File.
+   */
+  void release();
+
+  /**
+   * Swap this File with another.
+   */
+  void swap(File& other);
+
+  // movable
+  File(File&&);
+  File& operator=(File&&);
+
+ private:
+  // unique
+  File(const File&) = delete;
+  File& operator=(const File&) = delete;
+
+  int fd_;
+  bool ownsFd_;
+};
+
+void swap(File& a, File& b);
+
+}  // namespace folly
+
+#endif /* FOLLY_FILE_H_ */
index a819a7b7c983b68f89537bae2db763d796baac08..234b0c18fcb72e356cee4a39445b29b555ae10d6 100644 (file)
@@ -17,7 +17,7 @@
 #ifndef FOLLY_FILEGEN_H_
 #define FOLLY_FILEGEN_H_
 
-#include "folly/experimental/File.h"
+#include "folly/File.h"
 #include "folly/experimental/Gen.h"
 #include "folly/io/IOBuf.h"
 
diff --git a/folly/test/FileTest.cpp b/folly/test/FileTest.cpp
new file mode 100644 (file)
index 0000000..0111e4b
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2013 Facebook, Inc.
+ *
+ * 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 "folly/File.h"
+
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+
+#include "folly/Benchmark.h"
+#include "folly/String.h"
+
+using namespace folly;
+
+namespace {
+void expectWouldBlock(ssize_t r) {
+  int savedErrno = errno;
+  EXPECT_EQ(-1, r);
+  EXPECT_EQ(EAGAIN, savedErrno) << errnoStr(errno);
+}
+void expectOK(ssize_t r) {
+  int savedErrno = errno;
+  EXPECT_LE(0, r) << ": errno=" << errnoStr(errno);
+}
+}  // namespace
+
+TEST(File, Simple) {
+  // Open a file, ensure it's indeed open for reading
+  char buf = 'x';
+  {
+    File f("/etc/hosts");
+    EXPECT_NE(-1, f.fd());
+    EXPECT_EQ(1, ::read(f.fd(), &buf, 1));
+    f.close();
+    EXPECT_EQ(-1, f.fd());
+  }
+}
+
+TEST(File, OwnsFd) {
+  // Wrap a file descriptor, make sure that ownsFd works
+  // We'll test that the file descriptor is closed by closing the writing
+  // end of a pipe and making sure that a non-blocking read from the reading
+  // end returns 0.
+
+  char buf = 'x';
+  int p[2];
+  expectOK(::pipe(p));
+  int flags = ::fcntl(p[0], F_GETFL);
+  expectOK(flags);
+  expectOK(::fcntl(p[0], F_SETFL, flags | O_NONBLOCK));
+  expectWouldBlock(::read(p[0], &buf, 1));
+  {
+    File f(p[1]);
+    EXPECT_EQ(p[1], f.fd());
+  }
+  // Ensure that moving the file doesn't close it
+  {
+    File f(p[1]);
+    EXPECT_EQ(p[1], f.fd());
+    File f1(std::move(f));
+    EXPECT_EQ(-1, f.fd());
+    EXPECT_EQ(p[1], f1.fd());
+  }
+  expectWouldBlock(::read(p[0], &buf, 1));  // not closed
+  {
+    File f(p[1], true);
+    EXPECT_EQ(p[1], f.fd());
+  }
+  ssize_t r = ::read(p[0], &buf, 1);  // eof
+  expectOK(r);
+  EXPECT_EQ(0, r);
+  ::close(p[0]);
+}
+