optimize read mechanics for remote servers using ioctl to determine number of bytes available for reading.

in general, this should provide an improvement which scales with the amount of data being transferred:
* small transfers will incur a small amount of overhead from potentially unneeded memory as I try to account for a bug in FIONREAD which returns a number that is smaller than the actual number of bytes available for read on a socket
* large transfers will no longer require any copies of the data

on systems which do not provide the FIONREAD ioctl(), old functionality will be used
this should work on windows, though I (obviously) can't test it myself

thus ends the longest commit message I have ever written


SVN revision: 66063
This commit is contained in:
Mike Blumenkrantz 2011-12-09 22:35:12 +00:00
parent e8a3c54734
commit 0b109fb5c2
5 changed files with 95 additions and 17 deletions

View File

@ -390,3 +390,7 @@
* Allow SSL certificates to be loaded for STARTTLS
* Added functions to set/get the hostname used for SSL certificate verification
* ecore_con_ssl_server_cafile_add() now accepts directories
2011-12-09 Mike Blumenkrantz
* Created optimized reading mechanism for remote server connections

View File

@ -1415,6 +1415,7 @@ AC_ARG_ENABLE([ipv6],
if test "x${have_ecore_con}" = "xyes" ; then
AC_CHECK_HEADERS([sys/ioctl.h winsock2.h])
# Verify IPV6 availability in headers
if test "x${want_ipv6}" != "xno" ; then
AC_CHECK_TYPES([struct ipv6_mreq],

View File

@ -38,6 +38,14 @@
# include <Evil.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
#endif
#include "Ecore.h"
#include "ecore_private.h"
#include "Ecore_Con.h"
@ -1049,7 +1057,11 @@ ecore_con_event_server_data(Ecore_Con_Server *svr, unsigned char *buf, int num,
Ecore_Con_Event_Server_Data *e;
e = ecore_con_event_server_data_alloc();
EINA_SAFETY_ON_NULL_RETURN(e);
if (!e)
{
if (!duplicate) free(buf);
return;
}
svr->event_count = eina_list_append(svr->event_count, e);
_ecore_con_server_timer_update(svr);
@ -1999,9 +2011,14 @@ _ecore_con_svr_tcp_handler(void *data,
static void
_ecore_con_cl_read(Ecore_Con_Server *svr)
{
int num = 0;
Eina_Bool lost_server = EINA_TRUE;
int num2 = 0, num = 0;
#ifdef FIONREAD
double lr;
unsigned char *buf = NULL;
#else
unsigned char buf[READBUFSIZ];
#endif
Eina_Bool lost_server = EINA_TRUE;
DBG("svr=%p", svr);
@ -2016,23 +2033,42 @@ _ecore_con_cl_read(Ecore_Con_Server *svr)
lost_server = EINA_FALSE;
_ecore_con_server_timer_update(svr);
}
#ifdef FIONREAD
# ifdef _WIN32
if (ioctlsocket(svr->fd, FIONREAD, &num)) goto error;
# else
errno = 0;
if (ioctl(svr->fd, FIONREAD, &num)) goto error;
# endif
if (!num) return; /* FIXME: this shouldn't happen */
lr = ecore_time_get();
if ((num < 100) && (lr - svr->last_read < 0.01)) num2 = READBUFSIZ / 8;
else if (num > READBUFSIZ) num2 = READBUFSIZ;
if (num2)
{
DBG("%d bytes available for read from ioctl(), trying size (%d) to avoid congestion", num, num2);
num = num2;
}
else
DBG("%d bytes available for read", num);
svr->last_read = lr;
buf = malloc(num);
if (!buf) goto error;
#else
num = sizeof(buf);
(void)num2;
#endif
if (svr->ecs_state || !(svr->type & ECORE_CON_SSL))
{
errno = 0;
num = read(svr->fd, buf, sizeof(buf));
num = read(svr->fd, buf, num);
/* 0 is not a valid return value for a tcp socket */
if ((num > 0) || ((num < 0) && (errno == EAGAIN)))
lost_server = EINA_FALSE;
else if (num < 0)
ecore_con_event_server_error(svr, strerror(errno));
if ((num < 0) || (errno && (errno != EAGAIN))) goto error;
}
else
{
num = ecore_con_ssl_server_read(svr, buf, sizeof(buf));
num = ecore_con_ssl_server_read(svr, buf, num);
/* this is not an actual 0 return, 0 here just means non-fatal error such as EAGAIN */
if (num >= 0)
lost_server = EINA_FALSE;
if (num < 0) goto error;
}
if ((!svr->delete_me) && (num > 0))
@ -2040,11 +2076,25 @@ _ecore_con_cl_read(Ecore_Con_Server *svr)
if (svr->ecs_state)
ecore_con_socks_read(svr, buf, num);
else
ecore_con_event_server_data(svr, buf, num, EINA_TRUE);
ecore_con_event_server_data(svr, buf, num,
#ifdef FIONREAD
EINA_FALSE
#else
EINA_TRUE
#endif
);
}
if (lost_server)
_ecore_con_server_kill(svr);
return;
error:
#ifndef _WIN32
/* FIXME: strerror on windows */
ecore_con_event_server_error(svr, strerror(errno));
#endif
#ifdef FIONREAD
free(buf);
#endif
_ecore_con_server_kill(svr);
}
static Eina_Bool

View File

@ -150,6 +150,7 @@ struct _Ecore_Con_Server
const char *proxyip;
int proxyport;
/* endsocks */
/* SSL */
const char *verify_name;
#if USE_GNUTLS
gnutls_session_t session;
@ -165,7 +166,9 @@ struct _Ecore_Con_Server
SSL *ssl;
int ssl_err;
#endif
/* ENDSSL */
double start_time;
double last_read;
Ecore_Timer *until_deletion;
double disconnect_time;
double client_disconnect_time;

View File

@ -103,11 +103,25 @@ ecore_con_socks_read(Ecore_Con_Server *svr, unsigned char *buf, int num)
DBG("SOCKS: %d bytes", num);
if (num < 8)
{
#ifdef FIONREAD
if (!svr->ecs_recvbuf)
svr->ecs_recvbuf = eina_binbuf_manage_new_length(buf, num);
else
eina_binbuf_append_length(svr->ecs_recvbuf, buf, num);
if (!svr->ecs_recvbuf) goto error;
#else
if (!svr->ecs_recvbuf) svr->ecs_recvbuf = eina_binbuf_new();
if (!svr->ecs_recvbuf) goto error;
eina_binbuf_append_length(svr->ecs_recvbuf, buf, num);
#endif
/* the slowest connection on earth */
if (eina_binbuf_length_get(svr->ecs_recvbuf) != 8) return;
if (eina_binbuf_length_get(svr->ecs_recvbuf) != 8)
{
#ifdef FIONREAD
free(buf);
#endif
return;
}
data = eina_binbuf_string_get(svr->ecs_recvbuf);
}
else if (num > 8) goto error;
@ -149,6 +163,9 @@ ecore_con_socks_read(Ecore_Con_Server *svr, unsigned char *buf, int num)
svr->ecs_state = ECORE_CON_SOCKS_STATE_DONE;
INF("PROXY CONNECTED");
if (svr->ecs_recvbuf) eina_binbuf_free(svr->ecs_recvbuf);
#ifdef FIONREAD
else free(buf);
#endif
svr->ecs_recvbuf = NULL;
svr->ecs_buf_offset = svr->ecs_addrlen = 0;
memset(svr->ecs_addr, 0, sizeof(svr->ecs_addr));
@ -159,6 +176,9 @@ ecore_con_socks_read(Ecore_Con_Server *svr, unsigned char *buf, int num)
}
return;
error:
#ifdef FIONREAD
free(buf);
#endif
_ecore_con_server_kill(svr);
}