Skip site navigation (1)Skip section navigation (2)
Date:      30 Jun 2001 11:39:49 +0200
From:      Dag-Erling Smorgrav <des@ofug.org>
To:        arch@freebsd.org
Subject:   New kqueue filter
Message-ID:  <xzp66deux0a.fsf@flood.ping.uio.no>

next in thread | raw e-mail | index | archive | help
--=-=-=

Attached is a patch that partially implements a kqueue filter that
waits for a particular character to show up in the input, as well as a
userland test program.  This could be extremely useful for writing an
fgetln() equivalent with a timeout (like libfetch's _fetch_getln(),
but with only a handful of syscalls instead of two or three per
character read).

I've written filter routines for ttys and sockets.  The tty code has
been tested; the socket code builds, but hasn't been tested.  Similar
code needs to be written for vnodes and pipes (I've started on pipes,
but haven't gotten very far); this could be a good project for a
junior kernel hacker.

The only problem I've experienced with this patch is that it seems the
filter always runs twice (even when it succeeds the first time, though
it only returns to userland the second time).  This is possibly a bug
in the kqueue framework code.

DES
-- 
Dag-Erling Smorgrav - des@ofug.org


--=-=-=
Content-Type: text/x-patch
Content-Disposition: attachment; filename=kqueue.diff

Index: sys/sys/event.h
===================================================================
RCS file: /home/ncvs/src/sys/sys/event.h,v
retrieving revision 1.12
diff -u -r1.12 event.h
--- sys/sys/event.h	2001/02/24 01:44:03	1.12
+++ sys/sys/event.h	2001/06/18 20:24:42
@@ -35,8 +35,9 @@
 #define EVFILT_VNODE		(-4)	/* attached to vnodes */
 #define EVFILT_PROC		(-5)	/* attached to struct proc */
 #define EVFILT_SIGNAL		(-6)	/* attached to struct proc */
+#define EVFILT_CHAR		(-7)
 
-#define EVFILT_SYSCOUNT		6
+#define EVFILT_SYSCOUNT		7
 
 #define EV_SET(kevp, a, b, c, d, e, f) do {	\
 	(kevp)->ident = (a);			\
Index: sys/kern/kern_event.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/kern_event.c,v
retrieving revision 1.26
diff -u -r1.26 kern_event.c
--- sys/kern/kern_event.c	2001/05/01 08:12:56	1.26
+++ sys/kern/kern_event.c	2001/06/18 20:24:42
@@ -122,6 +122,7 @@
 	&file_filtops,			/* EVFILT_VNODE */
 	&proc_filtops,			/* EVFILT_PROC */
 	&sig_filtops,			/* EVFILT_SIGNAL */
+	&file_filtops,			/* EVFILT_CHAR */
 };
 
 static int
Index: sys/kern/tty.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/tty.c,v
retrieving revision 1.153
diff -u -r1.153 tty.c
--- sys/kern/tty.c	2001/05/22 22:16:18	1.153
+++ sys/kern/tty.c	2001/06/20 21:03:18
@@ -79,6 +79,7 @@
 #include <sys/ioctl_compat.h>
 #endif
 #include <sys/proc.h>
+#include <sys/clist.h>
 #define	TTYDEFCHARS
 #include <sys/tty.h>
 #undef	TTYDEFCHARS
@@ -114,6 +115,7 @@
 static void 	filt_ttyrdetach __P((struct knote *kn));
 static int	filt_ttywrite __P((struct knote *kn, long hint));
 static void 	filt_ttywdetach __P((struct knote *kn));
+static int	filt_ttychar __P((struct knote *kn, long hint));
 
 /*
  * Table with character classes and parity. The 8th bit indicates parity,
@@ -1102,6 +1104,8 @@
 	{ 1, NULL, filt_ttyrdetach, filt_ttyread };
 static struct filterops ttywrite_filtops =
 	{ 1, NULL, filt_ttywdetach, filt_ttywrite };
+static struct filterops ttychar_filtops =
+	{ 1, NULL, filt_ttyrdetach, filt_ttychar };
 
 int
 ttykqfilter(dev, kn)
@@ -1121,6 +1125,10 @@
 		klist = &tp->t_wsel.si_note;
 		kn->kn_fop = &ttywrite_filtops;
 		break;
+	case EVFILT_CHAR:
+		klist = &tp->t_rsel.si_note;
+		kn->kn_fop = &ttychar_filtops;
+		break;
 	default:
 		return (1);
 	}
@@ -1179,6 +1187,46 @@
 		return (1);
 	return (kn->kn_data <= tp->t_olowat &&
 	    ISSET(tp->t_state, TS_CONNECTED));
+}
+
+static int
+filt_ttychar(struct knote *kn, long hint)
+{
+	struct tty *tp = ((dev_t)kn->kn_hook)->si_tty;
+	struct cblock *cbp;
+	intptr_t offset, limit;
+	u_char ch, *end, *p;
+
+	if (tp->t_canq.c_cc == 0)
+		return (0);
+
+	ch = kn->kn_sfflags;
+	limit = kn->kn_sdata;
+	
+	cbp = (struct cblock *)((intptr_t)tp->t_canq.c_cf & ~CROUND);
+	p = p = tp->t_canq.c_cf;
+	offset = 0;
+	while (cbp != NULL) {
+		for (end = (u_char *)(cbp + 1); p < end; ++p) {
+			if (limit > 0 && ++offset > limit) {
+				kn->kn_data = -1;
+				return (1);
+			}
+			if (*p == ch) {
+				kn->kn_data = offset;
+				return (1);
+			}
+		}
+		p = tp->t_canq.c_cf;
+		cbp = cbp->c_next;
+	}
+	
+	if (ISSET(tp->t_state, TS_ZOMBIE)) {
+		kn->kn_flags |= EV_EOF;
+		return (1);
+	}
+
+	return (0);
 }
 
 /*
Index: sys/kern/uipc_socket.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/uipc_socket.c,v
retrieving revision 1.97
diff -u -r1.97 uipc_socket.c
--- sys/kern/uipc_socket.c	2001/05/01 08:12:58	1.97
+++ sys/kern/uipc_socket.c	2001/06/20 21:04:28
@@ -72,6 +72,7 @@
 static void 	filt_sowdetach(struct knote *kn);
 static int	filt_sowrite(struct knote *kn, long hint);
 static int	filt_solisten(struct knote *kn, long hint);
+static int	filt_sochar(struct knote *kn, long hint);
 
 static struct filterops solisten_filtops = 
 	{ 1, NULL, filt_sordetach, filt_solisten };
@@ -79,6 +80,8 @@
 	{ 1, NULL, filt_sordetach, filt_soread };
 static struct filterops sowrite_filtops = 
 	{ 1, NULL, filt_sowdetach, filt_sowrite };
+static struct filterops sochar_filtops =
+	{ 1, NULL, filt_sordetach, filt_sochar };
 
 struct	vm_zone *socket_zone;
 so_gen_t	so_gencnt;	/* generation count for sockets */
@@ -1560,6 +1563,10 @@
 		kn->kn_fop = &sowrite_filtops;
 		sb = &so->so_snd;
 		break;
+	case EVFILT_CHAR:
+		kn->kn_fop = &sochar_filtops;
+		sb = &so->so_rcv;
+		break;
 	default:
 		return (1);
 	}
@@ -1644,4 +1651,51 @@
 
 	kn->kn_data = so->so_qlen - so->so_incqlen;
 	return (! TAILQ_EMPTY(&so->so_comp));
+}
+
+/*ARGSUSED*/
+static int
+filt_sochar(struct knote *kn, long hint)
+{
+	struct socket *so = (struct socket *)kn->kn_fp->f_data;
+	struct mbuf *mb, *nmb;
+	intptr_t limit, offset;
+	u_char ch, *buf;
+	int i;
+	
+	if (so->so_rcv.sb_mb == NULL || so->so_rcv.sb_cc == 0)
+		return (0);
+
+	ch = kn->kn_sfflags;
+	limit = kn->kn_sdata;
+
+	mb = so->so_rcv.sb_mb;
+	nmb = mb->m_nextpkt;
+	offset = 0;
+	while (mb != NULL) {
+		buf = mtod(mb, u_char *);
+		for (i = 0; i < mb->m_len; ++i) {
+			if (limit > 0 && ++offset > limit) {
+				kn->kn_data = -1;
+				return (1);
+			}
+			if (buf[i] == ch) {
+				kn->kn_data = offset;
+				return (1);
+			}
+		}
+		
+		if ((mb = mb->m_next) == NULL) {
+			if ((mb = nmb) != NULL)
+				nmb = mb->m_nextpkt;
+		}
+	}
+
+	if (so->so_state & SS_CANTRCVMORE) {
+		kn->kn_flags |= EV_EOF; 
+		kn->kn_fflags = so->so_error;
+		return (1);
+	}
+
+	return (0);
 }

--=-=-=
Content-Type: text/plain; charset=iso-8859-1
Content-Disposition: attachment; filename=kchar.c
Content-Transfer-Encoding: quoted-printable

/*-
 * Copyright (c) 2001 Dag-Erling Co=EFdan Sm=F8rgrav
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer
 *    in this position and unchanged.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *      $FreeBSD$
 */

#include <sys/types.h>
#include <sys/errno.h>
#include <sys/event.h>
#include <sys/time.h>

#include <stdio.h>
#include <unistd.h>

#define MAXLINE 32

static int timeout =3D 60;

int
main(int argc, char *argv[])
{
	unsigned char line[MAXLINE];
	struct timeval now, deadline;
	struct timespec wait;
	struct kevent kev;
	int ifd, kq, len, n, o;

	ifd =3D STDIN_FILENO;

	while ((o =3D getopt(argc, argv, "t:")) !=3D -1)
		switch (o) {
		case 't':
			timeout =3D atoi(optarg);
			break;
		default:
			err(1, "Usage: kchar [-t timeout]\n");
		}
=09
	if ((kq =3D kqueue()) =3D=3D -1)
		err("kqueue()");
=09
	EV_SET(&kev, ifd, EVFILT_CHAR, EV_ADD|EV_CLEAR, '\n', MAXLINE, 0);
=09
	wait.tv_sec =3D 0;
	wait.tv_nsec =3D 0;
	if (kevent(kq, &kev, 1, NULL, 0, &wait) =3D=3D -1)
		err("kevent()");

	printf("Type some stuff:\n");
	fflush(stdout);

	gettimeofday(&now, NULL);
	deadline =3D now;
	deadline.tv_sec +=3D timeout;
=09
	for (;;) {
		gettimeofday(&now, NULL);
		wait.tv_sec =3D deadline.tv_sec - now.tv_sec;
		wait.tv_nsec =3D 1000 * (deadline.tv_usec - now.tv_usec);
		if (wait.tv_nsec < 0) {
			wait.tv_nsec +=3D 1000000000;
			wait.tv_sec -=3D 1;
		}
		n =3D kevent(kq, NULL, 0, &kev, 1, &wait);
		printf("kevent() returns %d\n", n);
		if (n =3D=3D 0) {
			warnx("timeout!");
			goto error;
		} else if (n < 0) {
			if (errno =3D=3D EINTR)
				continue;
			warn("kevent()");
			goto error;
		}
		break;
	}
	close(kq);
	len =3D kev.data;
	if (len < 1) {
		printf("too many characters\n");
		goto error;
	} else if (len > 0) {
		printf("found at %d!\n", len);
		if (read(ifd, line, len) < 0)
			err("read()");
		line[--len] =3D '\0';
	}
	printf("[%s] %d 0x%04x\n", line, len, kev.flags);
error:
	while (getchar() !=3D EOF)
		/* nothing */ ;
	exit(0);
}

--=-=-=--

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-arch" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?xzp66deux0a.fsf>