Add TFDTransport: a dead-simple wrapper around a file-descriptor.


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665644 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/cpp/Makefile.am b/lib/cpp/Makefile.am
index 571c4ff..feaebaf 100644
--- a/lib/cpp/Makefile.am
+++ b/lib/cpp/Makefile.am
@@ -34,6 +34,7 @@
                        src/protocol/TJSONProtocol.cpp \
                        src/protocol/TBase64Utils.cpp \
                        src/transport/TTransportException.cpp \
+                       src/transport/TFDTransport.cpp \
                        src/transport/TFileTransport.cpp \
                        src/transport/THttpClient.cpp \
                        src/transport/TSocket.cpp \
@@ -94,6 +95,7 @@
 
 include_transportdir = $(include_thriftdir)/transport
 include_transport_HEADERS = \
+                         src/transport/TFDTransport.h \
                          src/transport/TFileTransport.h \
                          src/transport/TServerSocket.h \
                          src/transport/TServerTransport.h \
diff --git a/lib/cpp/src/transport/TFDTransport.cpp b/lib/cpp/src/transport/TFDTransport.cpp
new file mode 100644
index 0000000..bbdcb19
--- /dev/null
+++ b/lib/cpp/src/transport/TFDTransport.cpp
@@ -0,0 +1,64 @@
+// Copyright (c) 2006- Facebook
+// Distributed under the Thrift Software License
+//
+// See accompanying file LICENSE or visit the Thrift site at:
+// http://developers.facebook.com/thrift/
+
+#include <cerrno>
+#include <exception>
+
+#include <transport/TFDTransport.h>
+
+#include <unistd.h>
+
+using namespace std;
+
+namespace facebook { namespace thrift { namespace transport {
+
+void TFDTransport::close() {
+  if (!isOpen()) {
+    return;
+  }
+
+  int rv = ::close(fd_);
+  int errno_copy = errno;
+  fd_ = -1;
+  // Have to check uncaught_exception because this is called in the destructor.
+  if (rv < 0 && !std::uncaught_exception()) {
+    throw TTransportException(TTransportException::UNKNOWN,
+                              "TFDTransport::close()",
+                              errno_copy);
+  }
+}
+
+uint32_t TFDTransport::read(uint8_t* buf, uint32_t len) {
+  ssize_t rv = ::read(fd_, buf, len);
+  if (rv < 0) {
+    int errno_copy = errno;
+    throw TTransportException(TTransportException::UNKNOWN,
+                              "TFDTransport::read()",
+                              errno_copy);
+  }
+  return rv;
+}
+
+void TFDTransport::write(const uint8_t* buf, uint32_t len) {
+  while (len > 0) {
+    ssize_t rv = ::write(fd_, buf, len);
+
+    if (rv < 0) {
+      int errno_copy = errno;
+      throw TTransportException(TTransportException::UNKNOWN,
+                                "TFDTransport::write()",
+                                errno_copy);
+    } else if (rv == 0) {
+      throw TTransportException(TTransportException::END_OF_FILE,
+                                "TFDTransport::write()");
+    }
+
+    buf += rv;
+    len -= rv;
+  }
+}
+
+}}} // facebook::thrift::transport
diff --git a/lib/cpp/src/transport/TFDTransport.h b/lib/cpp/src/transport/TFDTransport.h
new file mode 100644
index 0000000..e70f987
--- /dev/null
+++ b/lib/cpp/src/transport/TFDTransport.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2006- Facebook
+// Distributed under the Thrift Software License
+//
+// See accompanying file LICENSE or visit the Thrift site at:
+// http://developers.facebook.com/thrift/
+
+#ifndef _THRIFT_TRANSPORT_TFDTRANSPORT_H_
+#define _THRIFT_TRANSPORT_TFDTRANSPORT_H_ 1
+
+#include <string>
+#include <sys/time.h>
+
+#include "TTransport.h"
+#include "TServerSocket.h"
+
+namespace facebook { namespace thrift { namespace transport {
+
+/**
+ * Dead-simple wrapper around a file descriptor.
+ *
+ * @author David Reiss <dreiss@facebook.com>
+ */
+class TFDTransport : public TTransport {
+ public:
+  enum ClosePolicy {
+    NO_CLOSE_ON_DESTROY = 0,
+    CLOSE_ON_DESTROY = 1,
+  };
+
+  TFDTransport(int fd, ClosePolicy close_policy = NO_CLOSE_ON_DESTROY)
+    : fd_(fd)
+    , close_policy_(close_policy)
+  {}
+
+  ~TFDTransport() {
+    if (close_policy_ == CLOSE_ON_DESTROY) {
+      close();
+    }
+  }
+
+  bool isOpen() { return fd_ >= 0; }
+
+  void open() {}
+
+  void close();
+
+  uint32_t read(uint8_t* buf, uint32_t len);
+
+  void write(const uint8_t* buf, uint32_t len);
+
+  void setFD(int fd) { fd_ = fd; }
+  int getFD() { return fd_; }
+
+ protected:
+  int fd_;
+  ClosePolicy close_policy_;
+};
+
+}}} // facebook::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TFDTRANSPORT_H_
diff --git a/test/Makefile.am b/test/Makefile.am
index 5de2f1b..5490de8 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -27,6 +27,7 @@
 Benchmark_LDADD = libtestgencpp.la
 
 check_PROGRAMS = \
+	TFDTransportTest \
 	DebugProtoTest \
 	JSONProtoTest \
 	OptionalRequiredTest \
@@ -43,6 +44,15 @@
 UnitTests_LDADD = libtestgencpp.la
 
 #
+# TFDTransportTest
+#
+TFDTransportTest_SOURCES = \
+	TFDTransportTest.cpp
+
+TFDTransportTest_LDADD = \
+	$(top_srcdir)/lib/cpp/libthrift.la
+
+#
 # DebugProtoTest
 #
 DebugProtoTest_SOURCES = \
diff --git a/test/TFDTransportTest.cpp b/test/TFDTransportTest.cpp
new file mode 100644
index 0000000..dfc2d27
--- /dev/null
+++ b/test/TFDTransportTest.cpp
@@ -0,0 +1,37 @@
+#include <cstdlib>
+#include <stdexcept>
+#include <Thrift.h>
+#include <transport/TFDTransport.h>
+using facebook::thrift::transport::TTransportException;
+using facebook::thrift::transport::TFDTransport;
+
+class DummyException : std::exception {
+};
+
+int main() {
+  {
+    TFDTransport t(256, TFDTransport::NO_CLOSE_ON_DESTROY);
+  }
+
+  try {
+    {
+      TFDTransport t(256, TFDTransport::CLOSE_ON_DESTROY);
+    }
+    std::abort();
+  } catch (TTransportException) {
+  }
+
+  try {
+    {
+      TFDTransport t(256, TFDTransport::CLOSE_ON_DESTROY);
+      throw DummyException();
+    }
+    std::abort();
+  } catch (TTransportException&) {
+    abort();
+  } catch (DummyException&) {
+  }
+
+  return 0;
+
+}