Skip to content

Commit

Permalink
folly/{experimental => .}/File
Browse files Browse the repository at this point in the history
Summary: Moving File into `/folly`.

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

Reviewed By: philipp@fb.com

FB internal diff: D714462
  • Loading branch information
ddrcoder authored and jdelong committed Mar 19, 2013
1 parent 4f7a54f commit feca2a7
Show file tree
Hide file tree
Showing 4 changed files with 294 additions and 1 deletion.
106 changes: 106 additions & 0 deletions folly/File.cpp
Original file line number Diff line number Diff line change
@@ -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
101 changes: 101 additions & 0 deletions folly/File.h
Original file line number Diff line number Diff line change
@@ -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_ */
2 changes: 1 addition & 1 deletion folly/experimental/FileGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
86 changes: 86 additions & 0 deletions folly/test/FileTest.cpp
Original file line number Diff line number Diff line change
@@ -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]);
}

0 comments on commit feca2a7

Please sign in to comment.