mirror of
https://github.com/darlinghq/darling-libkqueue.git
synced 2024-11-23 03:39:51 +00:00
* 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:
commit
27ccea6a1c
13
ChangeLog
Normal file
13
ChangeLog
Normal 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.
|
10
Makefile
10
Makefile
@ -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
4
configure
vendored
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
145
os/linux/timer.c
145
os/linux/timer.c
@ -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
2
test/configure
vendored
@ -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 ""
|
||||
|
@ -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);
|
||||
|
116
test/timer.c
116
test/timer.c
@ -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);
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user