Subject: Re: readv() questions
From: [email-masked]
To: [email-masked]
Cc: [email-masked]
Date: Wed, 10 May 2006 04:19:19 -0700

On Tue, May 09, 2006 at 10:11:35PM -0700, [email-masked] wrote:
> 
> So far so good, but if anyone sees anything glaring might as well point it
> out - although this is not really CW related at this point.
> 
> -cl

Just to follow up on this.. I did take your advice fully Dave, and decided
to just modify the partial iovec in place and restore a copy of it after
handling the partial situation. Reason being that it's beneficial to set
an iovec array once and pass it to read or writev on every call (if one
is using relatively non changing locations assigned to each iov_base per
vector - if not, it still works anyways). So far so good, I setup a test
case and have transfered over 20,000,000 records via 6 iovecs each at
around ~20-70 bytes average record size. I've also verified that partials
are occuring, in addition to both mid-vector and mid-array. 200K/sec - so
I think we can rest assured race conditions are out of the picture. My
code was the culprit, cygwin's was not :).

In case anyone needs the code for any purpose (I know that I searched usenet
and web quite a bit looking for any references to short|trunc|partial, etc.
WRT readv()/writev() and found very little, so this may come in handy to
others.

-cl

/* header */
#ifndef __NIOV_H
#define __NIOV_H

#define n_recv_iov(a,b,c,d) \
	n_iov_generic(a,b,c,d,N_IOV_RECV)
#define n_send_iov(a,b,c,d) \
	n_iov_generic(a,b,c,d,N_IOV_SEND)

enum n_iov_type__ {
	N_IOV_RECV,
	N_IOV_SEND
};
typedef enum n_iov_type__ n_iov_type;

ssize_t n_iov_generic(const int, struct iovec *, ssize_t, const int, n_iov_type);
ssize_t n_iov_offset(const struct iovec *, const ssize_t, ssize_t *);
ssize_t n_iov_total(const struct iovec *, const ssize_t);

#endif /* __NIOV_H */

/* module */
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#include "niov.h"

#define _POSIX_SOURCE 1

ssize_t n_iov_offset(const struct iovec *v, const ssize_t c, ssize_t *os)
{
	ssize_t		l, cv;

	/*
	 * compute filled iovec count and set any partial offset in "os".
	 * "os" should point to the number of bytes previously read when
	 * called.
	 */
	for (cv = 0; cv < c; cv++) {
		if ((l = v[cv].iov_len) > *os) break;
		*os -= l;
	}

	return cv;
}

ssize_t n_iov_total(const struct iovec *v, const ssize_t c)
{
	ssize_t		cv, bt;

	for (cv = bt = 0; cv < c; cv++)
		bt += v[cv].iov_len;

	return bt;
}

ssize_t n_iov_generic(const int s, struct iovec *v, ssize_t c, const int t, n_iov_type nt)
{
	struct timeval	to;
	struct iovec	iov_o = { NULL, 0 };
	fd_set		fds, fds_m, *rfds = NULL, *wfds = NULL;
	ssize_t		(*nf_iov)(int, const struct iovec *, ssize_t);
	ssize_t		cv, b, be, bt;
	int		res;

	switch (nt) {
	case N_IOV_RECV:
		nf_iov = readv;
		rfds = &fds;
		break;
	case N_IOV_SEND:
		nf_iov = writev;
		wfds = &fds;
		break;
	default: break;
	}

	FD_ZERO(&fds_m);
	FD_SET(s, &fds_m);

	for (bt = 0, be = n_iov_total(v, c); bt < be; ) {
		fds = fds_m;
		to.tv_sec = t;
		to.tv_usec = 0;

		b = nf_iov(s, v, c);

		if (b == -1) {
			switch (errno) {
			case EWOULDBLOCK:
			case EINTR:
				break;
			default:
				perror(nt == N_IOV_RECV ? "readv" : "writev");
				bt = -1;
				break;
			}

			if (bt == -1) break;
		} else if (b && (bt += b) < be) {
			/*
			 * short count situation.
			 *
			 * if not within mid-vector, advance past filled, else
			 * temporarily modify original iovec to make up the
			 * difference and restore on exit.
			 */

			/* if previously saved, restore before modify */
			if (iov_o.iov_base) {
				memcpy(v, &iov_o, sizeof(*v));
				iov_o.iov_base = NULL;
			}

			/*
			 * cv = filled vectors returned, advance past these.
			 * b = mid-vector offset in case of partial vector.
			 */
			cv = n_iov_offset(v, c, &b);
			v += cv;
			c -= cv;

			/* if mid-vector, save original before modifying */
			if (b) {
				memcpy(&iov_o, v, sizeof(*v));
				v->iov_base = (char *)v->iov_base + b;
				v->iov_len -= b;
			}

			continue;
		} else {
			break;
		}

		if ((res = select(s + 1, rfds, wfds, NULL, &to)) == 0) {
			bt = -1;		/* timeout */
			break;
		} else if (res == -1) {
			perror("select");	/* never happen */
			bt = -1;
			break;
		}
	}

	/* restore original if still modified */
	if (iov_o.iov_base)
		memcpy(v, &iov_o, sizeof(iov_o));

	return bt;
}

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/