From: Tom Jackson Date: Thu, 21 Feb 2013 00:50:50 +0000 (-0800) Subject: folly/{experimental => .}/File X-Git-Tag: v0.22.0~1056 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=feca2a76e57bec55f894f7092fa2ea85ef5861d1;p=folly.git folly/{experimental => .}/File Summary: Moving File into `/folly`. Test Plan: Same unit tests, rebuild affected code outside folly. Reviewed By: philipp@fb.com FB internal diff: D714462 --- diff --git a/folly/File.cpp b/folly/File.cpp new file mode 100644 index 00000000..b2f8900e --- /dev/null +++ b/folly/File.cpp @@ -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 + +#include + +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 index 00000000..1f30c988 --- /dev/null +++ b/folly/File.h @@ -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 +#include +#include + +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_ */ diff --git a/folly/experimental/FileGen.h b/folly/experimental/FileGen.h index a819a7b7..234b0c18 100644 --- a/folly/experimental/FileGen.h +++ b/folly/experimental/FileGen.h @@ -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 index 00000000..0111e4b9 --- /dev/null +++ b/folly/test/FileTest.cpp @@ -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 +#include + +#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]); +} +