Rev 2 of Thrift, the Pillar successor
Summary: End-to-end communications and serialization in C++ is working
Reviewed By: aditya
Test Plan: See the new top-level test/ folder. It vaguely resembles a unit test, though it could be more automated.
Revert Plan: Revertible
Notes: Still a LOT of optimization work to be done on the generated C++ code, which should be using dynamic memory in a number of places. Next major task is writing the PHP/Java/Python generators.
git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664712 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/cpp/transport/TSocket.cc b/lib/cpp/transport/TSocket.cc
new file mode 100644
index 0000000..1dfe431
--- /dev/null
+++ b/lib/cpp/transport/TSocket.cc
@@ -0,0 +1,180 @@
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "transport/TSocket.h"
+
+using namespace std;
+
+// Mutex to protect syscalls to netdb
+pthread_mutex_t g_netdb_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// TODO(mcslee): Make this an option to the socket class
+#define MAX_RECV_RETRIES 20
+
+TSocket::TSocket(string host, int port) :
+ host_(host), port_(port), socket_(0) {}
+
+TSocket::TSocket(int socket) {
+ socket_ = socket;
+}
+
+TSocket::~TSocket() {
+ close();
+}
+
+bool TSocket::open() {
+ // Create socket
+ socket_ = socket(AF_INET, SOCK_STREAM, 0);
+ if (socket_ == -1) {
+ socket_ = 0;
+ return false;
+ }
+
+ // Lookup the host
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port_);
+
+ /*
+ if (inet_pton(AF_INET, host_.c_str(), &addr.sin_addr) < 0) {
+ perror("TSocket::open() inet_pton");
+ }
+ */
+
+ {
+ // TODO(mcslee): Fix scope-locking here to protect hostname lookups
+ // scopelock sl(&netdb_mutex);
+ struct hostent *host_entry = gethostbyname(host_.c_str());
+
+ if (host_entry == NULL) {
+ // perror("dns error: failed call to gethostbyname.\n");
+ close();
+ return false;
+ }
+
+ addr.sin_port = htons(port_);
+ memcpy(&addr.sin_addr.s_addr,
+ host_entry->h_addr_list[0],
+ host_entry->h_length);
+ }
+
+ // Connect the socket
+ int ret = connect(socket_, (struct sockaddr *)&addr, sizeof(addr));
+
+ // Connect failed
+ if (ret < 0) {
+ perror("TSocket::open() connect");
+ close();
+ return false;
+ }
+
+ return true;
+}
+
+void TSocket::close() {
+ if (socket_ > 0) {
+ shutdown(socket_, SHUT_RDWR);
+ ::close(socket_);
+ }
+ socket_ = 0;
+}
+
+int TSocket::read(string& s, uint32_t len) {
+ char buff[len];
+ s = "";
+
+ uint32_t have = 0;
+ uint32_t retries = 0;
+
+ while (have < len) {
+ try_again:
+ // Read from the socket
+ int got = recv(socket_, buff+have, len-have, 0);
+
+ // Check for error on read
+ if (got < 0) {
+ perror("TSocket::read()");
+
+ // If temporarily out of resources, sleep a bit and try again
+ if (errno == EAGAIN && retries++ < MAX_RECV_RETRIES) {
+ usleep(50);
+ goto try_again;
+ }
+
+ // If interrupted, try again
+ if (errno == EINTR && retries++ < MAX_RECV_RETRIES) {
+ goto try_again;
+ }
+
+ // If we disconnect with no linger time
+ if (errno == ECONNRESET) {
+ return 0;
+ }
+
+ return 0;
+ }
+
+ // Check for empty read
+ if (got == 0) {
+ return 0;
+ }
+
+ // Update the count
+ have += (uint32_t) got;
+ }
+
+ // Pack data into string
+ s = string(buff, have);
+ return have;
+}
+
+void TSocket::write(const string& s) {
+ uint32_t sent = 0;
+
+ while (sent < s.size()) {
+ int b = send(socket_, s.data() + sent, s.size() - sent, 0);
+
+ // Fail on a send error
+ if (b < 0) {
+ // TODO(mcslee): Make the function return how many bytes it wrote or
+ // throw an exception
+ // throw_perror("send");
+ return;
+ }
+
+ // Fail on blocked send
+ if (b == 0) {
+ // TODO(mcslee): Make the function return how many bytes it wrote or
+ // throw string("couldn't send data.\n");
+ return;
+ }
+
+ sent += b;
+ }
+}
+
+bool TSocket::setLinger(bool on, int linger) {
+ struct linger ling = {(on ? 1 : 0), linger};
+ if (-1 == setsockopt(socket_, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling))) {
+ close();
+ perror("TSocket::setLinger()");
+ return false;
+ }
+ return true;
+}
+
+bool TSocket::setNoDelay(bool noDelay) {
+ // Set socket to NODELAY
+ int val = (noDelay ? 1 : 0);
+ if (-1 == setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val))) {
+ close();
+ perror("TSocket::setNoDelay()");
+ return false;
+ }
+ return true;
+}