From 617cba0e058c0d17be574ae7556f09bfc5223620 Mon Sep 17 00:00:00 2001
From: Mattias Andrée <maandree@operamail.com>
Date: Tue, 25 Aug 2015 23:10:26 +0200
Subject: implement libmds_connection_send_unlocked
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Mattias Andrée <maandree@operamail.com>
---
 src/libmdsclient/comm.c | 88 ++++++++++++++++++++++++++++++++++++++++++++-----
 src/libmdsclient/comm.h | 49 +++++++++++++++++++++++----
 2 files changed, 121 insertions(+), 16 deletions(-)

diff --git a/src/libmdsclient/comm.c b/src/libmdsclient/comm.c
index 3284043..a01d1d7 100644
--- a/src/libmdsclient/comm.c
+++ b/src/libmdsclient/comm.c
@@ -19,6 +19,12 @@
 
 #include <stdlib.h>
 #include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+
+
+#define min(a, b)  ((a) < (b) ? (a) : (b))
 
 
 
@@ -119,19 +125,32 @@ void libmds_connection_free(libmds_connection_t* restrict this)
  * @param   this     The connection descriptor, must not be `NULL`
  * @param   message  The message to send, must not be `NULL`
  * @param   length   The length of the message, should be positive
- * @return           Zero on success, -1 on error, `ernno`
- *                   will have been set accordingly on error
+ * @return           The number of sent bytes. Less than `length` on error,
+ *                   `ernno` will have been set accordingly on error
  * 
- * @throws  See pthread_mutex_lock(3)
+ * @throws  EACCES        See send(2)
+ * @throws  EWOULDBLOCK   See send(2), only if the socket has been modified to nonblocking
+ * @throws  EBADF         See send(2)
+ * @throws  ECONNRESET    If connection was lost
+ * @throws  EDESTADDRREQ  See send(2)
+ * @throws  EFAULT        See send(2)
+ * @throws  EINVAL        See send(2)
+ * @throws  ENOBUFS       See send(2)
+ * @throws  ENOMEM        See send(2)
+ * @throws  ENOTCONN      See send(2)
+ * @throws  ENOTSOCK      See send(2)
+ * @throws  EPIPE         See send(2)
+ * @throws                See pthread_mutex_lock(3)
  */
-int libmds_connection_send(libmds_connection_t* restrict this, const char* message, size_t length)
+size_t libmds_connection_send(libmds_connection_t* restrict this, const char* message, size_t length)
 {
-  int r, saved_errno;
+  int saved_errno;
+  size_t r;
   
   if (libmds_connection_lock(this))
-    return -1;
+    return 0;
   
-  r = libmds_connection_send_unlocked(this, message, length);
+  r = libmds_connection_send_unlocked(this, message, length, 1);
   
   saved_errno = errno;
   (void) libmds_connection_unlock(this);
@@ -139,8 +158,59 @@ int libmds_connection_send(libmds_connection_t* restrict this, const char* messa
 }
 
 
-int libmds_connection_send_unlocked(libmds_connection_t* restrict this, const char* message, size_t length)
+/**
+ * Send a message to the display server, without locking the
+ * mutex of the conncetion
+ * 
+ * @param   this                   The connection descriptor, must not be `NULL`
+ * @param   message                The message to send, must not be `NULL`
+ * @param   length                 The length of the message, should be positive
+ * @param   continue_on_interrupt  Whether to continue sending if interrupted by a signal
+ * @return                         The number of sent bytes. Less than `length` on error,
+ *                                 `ernno` will have been set accordingly on error
+ * 
+ * @throws  EACCES        See send(2)
+ * @throws  EWOULDBLOCK   See send(2), only if the socket has been modified to nonblocking
+ * @throws  EBADF         See send(2)
+ * @throws  ECONNRESET    If connection was lost
+ * @throws  EDESTADDRREQ  See send(2)
+ * @throws  EFAULT        See send(2)
+ * @throws  EINTR         If interrupted by a signal, only if `continue_on_interrupt' is zero
+ * @throws  EINVAL        See send(2)
+ * @throws  ENOBUFS       See send(2)
+ * @throws  ENOMEM        See send(2)
+ * @throws  ENOTCONN      See send(2)
+ * @throws  ENOTSOCK      See send(2)
+ * @throws  EPIPE         See send(2)
+ */
+size_t libmds_connection_send_unlocked(libmds_connection_t* restrict this, const char* message,
+				       size_t length, int continue_on_interrupt)
 {
-  return (void) this, (void) message, (void) length, 0; /* TODO */
+  size_t block_size = length;
+  size_t sent = 0;
+  ssize_t just_sent;
+  
+  errno = 0;
+  while (length > 0)
+    if ((just_sent = send(this->socket_fd, message + sent, min(block_size, length), MSG_NOSIGNAL)) < 0)
+      {
+	if (errno == EMSGSIZE)
+	  {
+	    block_size >>= 1;
+	    if (block_size == 0)
+	      return sent;
+	  }
+	else if ((errno == EINTR) && continue_on_interrupt)
+	  continue;
+	else
+	  return sent;
+      }
+    else
+      {
+	sent += (size_t)just_sent;
+	length -= (size_t)just_sent;
+      }
+  
+  return sent;
 }
 
diff --git a/src/libmdsclient/comm.h b/src/libmdsclient/comm.h
index 7b2f46c..e982545 100644
--- a/src/libmdsclient/comm.h
+++ b/src/libmdsclient/comm.h
@@ -117,19 +117,54 @@ void libmds_connection_free(libmds_connection_t* restrict this);
  * @param   this     The connection descriptor, must not be `NULL`
  * @param   message  The message to send, must not be `NULL`
  * @param   length   The length of the message, should be positive
- * @return           Zero on success, -1 on error, `ernno`
- *                   will have been set accordingly on error
- * 
- * @throws  See pthread_mutex_lock(3)
+ * @return           The number of sent bytes. Less than `length` on error,
+ *                   `ernno` will have been set accordingly on error
+ * 
+ * @throws  EACCES        See send(2)
+ * @throws  EWOULDBLOCK   See send(2), only if the socket has been modified to nonblocking
+ * @throws  EBADF         See send(2)
+ * @throws  ECONNRESET    If connection was lost
+ * @throws  EDESTADDRREQ  See send(2)
+ * @throws  EFAULT        See send(2)
+ * @throws  EINVAL        See send(2)
+ * @throws  ENOBUFS       See send(2)
+ * @throws  ENOMEM        See send(2)
+ * @throws  ENOTCONN      See send(2)
+ * @throws  ENOTSOCK      See send(2)
+ * @throws  EPIPE         See send(2)
+ * @throws                See pthread_mutex_lock(3)
  */
 __attribute__((nonnull))
-int libmds_connection_send(libmds_connection_t* restrict this, const char* message, size_t length);
+size_t libmds_connection_send(libmds_connection_t* restrict this, const char* message, size_t length);
 
 /**
- * TODO doc
+ * Send a message to the display server, without locking the
+ * mutex of the conncetion
+ * 
+ * @param   this                   The connection descriptor, must not be `NULL`
+ * @param   message                The message to send, must not be `NULL`
+ * @param   length                 The length of the message, should be positive
+ * @param   continue_on_interrupt  Whether to continue sending if interrupted by a signal
+ * @return                         The number of sent bytes. Less than `length` on error,
+ *                                 `ernno` will have been set accordingly on error
+ * 
+ * @throws  EACCES        See send(2)
+ * @throws  EWOULDBLOCK   See send(2), only if the socket has been modified to nonblocking
+ * @throws  EBADF         See send(2)
+ * @throws  ECONNRESET    If connection was lost
+ * @throws  EDESTADDRREQ  See send(2)
+ * @throws  EFAULT        See send(2)
+ * @throws  EINTR         If interrupted by a signal, only if `continue_on_interrupt' is zero
+ * @throws  EINVAL        See send(2)
+ * @throws  ENOBUFS       See send(2)
+ * @throws  ENOMEM        See send(2)
+ * @throws  ENOTCONN      See send(2)
+ * @throws  ENOTSOCK      See send(2)
+ * @throws  EPIPE         See send(2)
  */
 __attribute__((nonnull))
-int libmds_connection_send_unlocked(libmds_connection_t* restrict this, const char* message, size_t length);
+size_t libmds_connection_send_unlocked(libmds_connection_t* restrict this, const char* message,
+				       size_t length, int continue_on_interrupt);
 
 /**
  * Lock the connection descriptor for being modified,
-- 
cgit v1.2.3-70-g09d2