--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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_ */
#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"
--- /dev/null
+/*
+ * 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]);
+}
+