inline bool BSDTCPConnection::write (const Byte* bytes, Size size) { assert(0 != bytes); assert(0 < size); do { ScopedProfiledTask _(ProfilerTask_SocketWriteLoop); report_false_if("connection: write: disconnecting", disconnecting); const int status = select_single_write(socket.fd, 100); if (0 == status) { continue; // Cannot write just yet. } else if (status < 0) { uplink_log_error("connection: write: select failed: %s", strerror(errno)); return false; } const ssize_t count = ::write(socket.fd, bytes, size); if (0 == count) { uplink_log_error("connection: write failed: EOF"); return false; } else if (count < 0) { uplink_log_error("connection: write failed: %s", strerror(errno)); return false; } size -= count; bytes += count; } while (0 < size); return true; }
inline bool BSDTCPConnection::read (Byte* bytes, Size size) { assert(0 != bytes); assert(0 < size); do { ScopedProfiledTask _(ProfilerTask_SocketReadLoop); report_false_if("connection: read: disconnecting", disconnecting); const int status = select_single_read(socket.fd, 100); if (0 == status) { continue; // Nothing to read, yet. } else if (status < 0) { uplink_log_error("connection: read: select: %s", strerror(errno)); return false; } const ssize_t count = ::read(socket.fd, bytes, size); if (0 == count) { uplink_log_error("connection: read: EOF"); return false; } else if (count < 0) { uplink_log_error("connection: read: %s", strerror(errno)); return false; } size -= count; bytes += count; } while (0 < size); return true; }
Impl(That* that) : that(that) , mainWindow(0) , sharingWindow(0) { if (!glfwInit()) { uplink_log_error("Failed to initialize GLFW."); abort(); } mainWindow = glfwCreateWindow( initialWindowWidth, initialWindowHeight, "Uplink", 0, 0 ); // Create a sharing context with a hidden window in order to allow other threads to upload data independently from the rendering thread. sharingWindow = glfwCreateWindow( 1, 1, "", 0, mainWindow ); glfwHideWindow(sharingWindow); glfwSetWindowUserPointer(mainWindow, this); glfwSetWindowSizeCallback(mainWindow, resize_callback); glfwSetKeyCallback(mainWindow, key_callback); glfwSwapInterval(1); glfwSetTime(0.0); glfwMakeContextCurrent(mainWindow); glGenTextures(1, &colorTextureId); glGenTextures(1, &depthTextureId); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glClearColor(0., 0., 0., 0.); glfwMakeContextCurrent(0); }
inline TCPConnection* TCPConnection_connect (CString host, uint16 port) { const int fd = socket(AF_INET, SOCK_STREAM, 0); report_zero_if(ERROR_MESSAGE("socket"), fd == -1); // Make it so that we can early-return from this scope using one-liners. struct Cleanup { explicit Cleanup (int fd) : ok(false), fd(fd) {} ~Cleanup () { return_if(ok); close(fd); } bool ok; int fd; } cleanup(fd); // FIXME: Use the IPv6-compliant getaddrinfo. struct hostent* ip = ::gethostbyname(host); report_zero_if(ERROR_MESSAGE("gethostbyname"), ip == NULL); sockaddr_in address; address.sin_family = AF_INET; memcpy(&address.sin_addr, ip->h_addr_list[0], ip->h_length); address.sin_port = htons(port); { // Put the socket into non-blocking mode. int flags = fcntl(fd, F_GETFL, 0); int status = fcntl(fd, F_SETFL, flags | O_NONBLOCK); report_zero_if(ERROR_MESSAGE("fcntl"), status < 0); } { // Perform the non-blocking connection dance. int status = 0; do { status = ::connect(fd, (sockaddr*) &address, sizeof(address)); } while (-1 == status && EINTR == errno); // In the extreme case where connect got interrupted by a signal. if (0 != status) // TCPConnection did not immediately succeed. { switch (errno) // Investigate the error, and take action. { case EINPROGRESS: // The connection is in progress. { static const int connectionTimeoutInMilliseconds = 500; // Poll the socket for writing, with a timeout. const int ret = select_single_write(fd, connectionTimeoutInMilliseconds); // Select might actually have returned EINPROGRESS, here. // Currently, we behave as if the connection failed when it times out. // FIXME: Introduce an asynchronous callback connection mechanism in parallel to this blocking one. // Abort if select failed. report_zero_unless(ERROR_MESSAGE("select"), 0 < ret); // Also abort if select returned with a descriptor not ready for writing. report_zero_if("Connection timed out.", 0 == ret); // Make sure that the connection is error-free. int err = 0; socklen_t len = sizeof(int); report_zero_unless(ERROR_MESSAGE("getsockopt"), 0 == getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len)); report_zero_unless(formatted("connect: %s", strerror(err)).get(), 0 == err); break; // The connection is now established. } default: // The connection failed for other reasons. { uplink_log_error(ERROR_MESSAGE("connect")); return 0; } } } } { // Put the socket back into blocking mode. int flags = fcntl(fd, F_GETFL, 0); int status = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); report_zero_if(ERROR_MESSAGE("fcntl"), status < 0); } cleanup.ok = true; return TCPConnection_create(fd); }