* Implement EVFILT_TIMER on Linux.

* Fix another 'make install' problem reported by Mario Schwalbe.

    * Do not link the test program with the pthreads library.

    * pkg-config no longer requires linking with -lpthread and -lrt.


git-svn-id: svn://svn.code.sf.net/p/libkqueue/code/branches/stable@62 fb4e3144-bc1c-4b72-a658-5bcd248dd7f7
This commit is contained in:
mheily 2009-11-07 21:03:21 +00:00
commit 27ccea6a1c
10 changed files with 220 additions and 100 deletions

13
ChangeLog Normal file
View File

@ -0,0 +1,13 @@
2009-11-10 v0.2 r59
* Implement EVFILT_TIMER on Linux.
* Fix another 'make install' problem reported by Mario Schwalbe.
* Do not link the test program with the pthreads library.
* pkg-config no longer requires linking with -lpthread and -lrt.
2009-05-05 v0.1 r49
* Initial stable release.

View File

@ -13,6 +13,7 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
REPOSITORY=svn+ssh://mark.heily.com/$$HOME/svn/$(PROGRAM)
INSTALL=/usr/bin/install
SOURCES=filter.c kevent.c knote.c kqueue.c
MANS=kqueue.2
@ -32,6 +33,7 @@ build:
install:
$(INSTALL) -d -m 755 $(INCLUDEDIR)/kqueue/sys
$(INSTALL) -m 644 sys/event.h $(INCLUDEDIR)/kqueue/sys/event.h
$(INSTALL) -d -m 755 $(LIBDIR)
$(INSTALL) -m 644 libkqueue.so $(LIBDIR)/libkqueue.so
$(INSTALL) -d -m 755 $(LIBDIR)/pkgconfig
$(INSTALL) -m 644 libkqueue.pc $(LIBDIR)/pkgconfig
@ -61,10 +63,16 @@ dist:
rm -rf $(PROGRAM)-$(VERSION)
publish-www:
rm -rf ~/public_html/libkqueue/ ; cp -R www ~/public_html/libkqueue/
rm ~/public_html/libkqueue/*.html ; cp -R www/*.html ~/public_html/libkqueue/
clean:
rm -f a.out *.a *.o *.so
merge:
svn diff $(REPOSITORY)/branches/stable $(REPOSITORY)/trunk | gvim -
@printf "Merge changes from the trunk to the stable branch [y/N]? "
@read x && test "$$x" = "y"
echo "ok"
distclean: clean
rm -f *.tar.gz config.mk config.h libkqueue.pc $(FILTERS)

4
configure vendored
View File

@ -1,7 +1,7 @@
#!/bin/sh
program=libkqueue
version=0.1
version=0.2
finalize() {
eval "if [ \"\$$1\" = \"\" ] ; then $1=\"$2\" ; fi"
@ -71,6 +71,7 @@ finalize libdir "${prefix}/lib"
finalize includedir "${prefix}/include"
finalize mandir "${prefix}/share/man"
finalize cflags "$default_cflags"
finalize libdepends ""
echo "Checking operating system type... $target"
rm -f socket.c vnode.c signal.c timer.c user.c
@ -95,6 +96,7 @@ sed -e "
s,@@LIBDIR@@,$libdir,g;
s,@@INCLUDEDIR@@,$includedir,g;
s,@@MANDIR@@,$mandir,g;
s,@@LIBDEPENDS@@,$libdepends,g;
" < $program.pc.in >> $program.pc
chmod 400 $program.pc

View File

@ -8,6 +8,6 @@ Description: Emulates FreeBSD kqueue(2) on other platforms
Version: @@VERSION@@
Requires:
Libs: -L${libdir} -lkqueue
Libs.private: -lpthread -lrt
Libs.private: @@LIBDEPENDS@@
Cflags: -I${includedir}/kqueue

View File

@ -34,42 +34,24 @@
#include "sys/event.h"
#include "private.h"
#if DEADWOOD
static void timer_convert(struct itimerspec *dst, int src);
/*
* Determine the smallest interval used by active timers.
*/
static int
update_timeres(struct filter *filt)
#if KQUEUE_DEBUG
static char *
itimerspec_dump(struct itimerspec *ts)
{
struct knote *kn;
struct itimerspec tval;
u_int cur = filt->kf_timeres;
char *buf;
/* Find the smallest timeout interval */
//FIXME not optimized
KNOTELIST_FOREACH(kn, &filt->knl) {
dbg_printf("cur=%d new=%d", cur, (int) kn->kev.data);
if (cur == 0 || kn->kev.data < cur)
cur = kn->kev.data;
}
if ((buf = calloc(1, 1024)) == NULL)
abort();
dbg_printf("cur=%d res=%d", cur, filt->kf_timeres);
if (cur == filt->kf_timeres)
return (0);
snprintf(buf, 1024,
"itimer: [ interval=%lu s %lu ns, next expire=%lu s %lu ns ]",
ts->it_interval.tv_sec,
ts->it_interval.tv_nsec,
ts->it_value.tv_sec,
ts->it_value.tv_nsec
);
dbg_printf("new timer interval = %d", cur);
filt->kf_timeres = cur;
/* Convert from miliseconds to seconds+nanoseconds */
timer_convert(&tval, cur);
if (timerfd_settime(filt->kf_pfd, 0, &tval, NULL) < 0) {
dbg_printf("signalfd(2): %s", strerror(errno));
return (-1);
}
return (0);
return (buf);
}
#endif
@ -77,23 +59,24 @@ update_timeres(struct filter *filt)
static void
convert_msec_to_itimerspec(struct itimerspec *dst, int src, int oneshot)
{
struct timespec now;
time_t sec, nsec;
/* Set the interval */
/* XXX-FIXME: this is probably horribly wrong :) */
sec = src / 1000;
nsec = (src % 1000) * 1000000;
dst->it_interval.tv_sec = sec;
dst->it_interval.tv_nsec = nsec;
dbg_printf("timer val=%lu %lu", sec, nsec);
/* FIXME: doesnt handle oneshot */
/* Set the interval */
if (oneshot) {
dst->it_interval.tv_sec = 0;
dst->it_interval.tv_nsec = 0;
} else {
dst->it_interval.tv_sec = sec;
dst->it_interval.tv_nsec = nsec;
}
/* Set the initial expiration */
clock_gettime(CLOCK_MONOTONIC, &now);
dst->it_value.tv_sec = now.tv_sec + sec;
dst->it_value.tv_nsec = now.tv_nsec + nsec;
dst->it_value.tv_sec = sec;
dst->it_value.tv_nsec = nsec;
dbg_printf("%s", itimerspec_dump(dst));
}
static int
@ -196,44 +179,70 @@ evfilt_timer_copyin(struct filter *filt,
return (0);
}
/* TODO: This entire function is copy+pasted from socket.c
with minor changes for timerfds.
Perhaps it could be refactored into a generic epoll_copyout()
that calls custom per-filter actions.
*/
int
evfilt_timer_copyout(struct filter *filt,
struct kevent *dst,
int nevents)
{
//struct knote *kn;
uint64_t buf;
int i;
struct epoll_event epevt[MAX_KEVENT];
struct epoll_event *ev;
struct knote *kn;
uint64_t expired;
int i, nret;
ssize_t n;
abort(); //TBD
n = read(filt->kf_pfd, &buf, sizeof(buf));
if (n < 0 || n < sizeof(buf)) {
dbg_puts("invalid read from timerfd");
return (-1);
for (;;) {
nret = epoll_wait(filt->kf_pfd, &epevt[0], nevents, 0);
if (nret < 0) {
if (errno == EINTR)
continue;
dbg_perror("epoll_wait");
return (-1);
} else {
break;
}
}
n = 1; // KLUDGE
//KNOTELIST_FOREACH(kn, &filt->knl) {
for (i = 0, nevents = 0; i < n; i++) {
#if FIXME
/* Want to have multiple timers, so maybe multiple timerfds... */
kn = knote_lookup(filt, sig[i].ssi_signo);
if (kn == NULL)
continue;
/* TODO: dst->data should be the number of times the signal occurred */
dst->ident = sig[i].ssi_signo;
dst->filter = EVFILT_SIGNAL;
for (i = 0, nevents = 0; i < nret; i++) {
ev = &epevt[i];
/* TODO: put in generic debug.c: epoll_event_dump(ev); */
kn = ev->data.ptr;
dst->ident = kn->kev.ident;
dst->filter = kn->kev.filter;
dst->udata = kn->kev.udata;
dst->flags = 0;
dst->flags = EV_ADD;
dst->fflags = 0;
dst->data = 1;
dst++;
if (kn->kev.flags & EV_ONESHOT) /*TODO: move elsewhere */
dst->flags |= EV_ONESHOT;
if (kn->kev.flags & EV_CLEAR) /*TODO: move elsewhere */
dst->flags |= EV_CLEAR;
if (ev->events & EPOLLERR)
dst->fflags = 1; /* FIXME: Return the actual timer error */
/* On return, data contains the number of times the
timer has been trigered.
*/
n = read(kn->kn_pfd, &expired, sizeof(expired));
if (n < 0 || n < sizeof(expired)) {
dbg_puts("invalid read from timerfd");
expired = 1; /* Fail gracefully */
}
dst->data = expired;
if (kn->kev.flags & EV_DISPATCH)
KNOTE_DISABLE(kn);
if (kn->kev.flags & EV_ONESHOT) {
ktimer_delete(filt, kn);
knote_free(kn);
}
nevents++;
#endif
dst++;
}
return (nevents);

2
test/configure vendored
View File

@ -89,7 +89,7 @@ cflags="-g -O0 -Wall -Werror"
ldadd=""
if [ "`uname -s`" = "Linux" ] ; then
cflags="$cflags -I.."
ldadd="$ldadd ../libkqueue.a -lpthread -lrt"
ldadd="$ldadd ../libkqueue.a"
fi
finalize cflags ""
finalize ldadd ""

View File

@ -220,10 +220,8 @@ main(int argc, char **argv)
test_evfilt_signal();
if (test_vnode)
test_evfilt_vnode();
#if FIXME
if (test_timer)
test_evfilt_timer();
#endif
puts("all tests completed.");
return (0);

View File

@ -45,15 +45,16 @@ test_kevent_timer_del(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
test_no_kevents();
success(test_id);
}
void
test_kevent_timer_get(void)
{
const char *test_id = "kevent(EVFILT_TIMER, get)";
const char *test_id = "kevent(EVFILT_TIMER, wait)";
struct kevent kev;
int nfds;
test_begin(test_id);
@ -61,11 +62,9 @@ test_kevent_timer_get(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
if (nfds < 1)
err(1, "%s", test_id);
if (kev.ident != 1 || kev.filter != EVFILT_TIMER)
errx(1, "wrong event");
kev.flags |= EV_CLEAR;
kev.data = 1;
kevent_cmp(&kev, kevent_get(kqfd));
EV_SET(&kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
@ -74,17 +73,104 @@ test_kevent_timer_get(void)
success(test_id);
}
static void
test_oneshot(void)
{
const char *test_id = "kevent(EVFILT_TIMER, EV_ONESHOT)";
struct kevent kev;
test_begin(test_id);
test_no_kevents();
EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 500,NULL);
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
/* Retrieve the event */
kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT;
kev.data = 1;
kevent_cmp(&kev, kevent_get(kqfd));
/* Check if the event occurs again */
sleep(3);
test_no_kevents();
success(test_id);
}
static void
test_periodic(void)
{
const char *test_id = "kevent(EVFILT_TIMER, periodic)";
struct kevent kev;
test_begin(test_id);
test_no_kevents();
EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, 1000,NULL);
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
/* Retrieve the event */
kev.flags = EV_ADD | EV_CLEAR;
kev.data = 1;
kevent_cmp(&kev, kevent_get(kqfd));
/* Check if the event occurs again */
sleep(1);
kevent_cmp(&kev, kevent_get(kqfd));
/* Delete the event */
kev.flags = EV_DELETE;
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
}
static void
disable_and_enable(void)
{
const char *test_id = "kevent(EVFILT_TIMER, EV_DISABLE and EV_ENABLE)";
struct kevent kev;
test_begin(test_id);
test_no_kevents();
/* Add the watch and immediately disable it */
EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 2000,NULL);
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
kev.flags = EV_DISABLE;
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
test_no_kevents();
/* Re-enable and check again */
kev.flags = EV_ENABLE;
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT;
kev.data = 1;
kevent_cmp(&kev, kevent_get(kqfd));
success(test_id);
}
void
test_evfilt_timer()
{
kqfd = kqueue();
test_kevent_timer_add();
test_kevent_timer_del();
test_kevent_timer_get();
#if TODO
test_kevent_signal_disable();
test_kevent_signal_enable();
test_kevent_signal_oneshot();
#endif
test_kevent_timer_add();
test_kevent_timer_del();
test_kevent_timer_get();
test_oneshot();
test_periodic();
disable_and_enable();
close(kqfd);
}

View File

@ -19,6 +19,8 @@ libkqueue is a portable userspace implementation of the <a href="http://www.free
<h2>Download</h2>
Source code releases can be found <a href="dist">here</a>.
<p>
To checkout the SVN repository, run the following command:
<p>
<code>svn checkout svn://mark.heily.com/libkqueue</code>
@ -68,8 +70,6 @@ libkqueue currently requires the following:
<h2>Usage</h2>
libkqueue is a multi-threaded library and <code>kqueue()</code> and <code>kevent()</code> are safe to be used from multiple threads.
Here are the steps to use libkqueue in your program:
<ol>
<li>Add <code>`pkg-config libkqueue --cflags`</code> to the CFLAGS variable.
@ -95,6 +95,10 @@ close the descriptor and free the resources:
#endif
</pre>
<h3>Threads</h3>
libkqueue is a threadsafe library, and <code>kqueue()</code> and <code>kevent()</code> are safe to be used from multiple threads. libkqueue requires that the POSIX threads headers (<code>pthread.h</code>) be available, but does not require you to link against <code>libpthread.so</code>. This allows it to be used in both non-threaded and multi-threaded programs.
<h2>Links</h2>
<ul>

View File

@ -55,11 +55,11 @@
<tr>
<td>EVFILT_TIMER<BR></td>
<td>Yes</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>N/A</td>
</tr>
<tr>
@ -96,13 +96,13 @@
READ / WRITE
NOTE_LOWAT No -- Not supported by Linux
VNODE
NOTE_DELETE Partial (fd must be closed)
NOTE_DELETE Yes
NOTE_WRITE Yes
NOTE_EXTEND No
NOTE_EXTEND Yes
NOTE_ATTRIB Yes
NOTE_LINK No
NOTE_LINK Yes
NOTE_RENAME Yes
NOTE_REVOKE N/A -- Not available in Linux
NOTE_REVOKE No -- Not available in Linux
USER
NOTE_FFNOP Ignore the input fflags.
NOTE_FFAND Bitwise AND fflags.