Hi,
as I've had an old version of libev (3.41) on a different machine, I ran
into the epoll spurious event notification bug.
I noticed the libev version to late and wrote example code to
reproduce the problem.
I guess it could be usefull for the epoll developers.
Basically,
* accept a connection A
* set A nonblocking
* recv something on A
* fork,
* in child run system() or execv
* close A
* accept a new connection B which gets the same fd as A had previously
leads to an endless loop with the read callback for B getting called all
time, even though there is no data to read.
Attached is a small programm which triggers the bug here on a linux
2.6.27 x86_64 smp machine.
Compile using
gcc -Wall -L/opt/libev/lib/libev/ -I/opt/libev/include spurious.c -o
spurious -lev
run it
./spurious
in a different terminal
nc localhost 4711
send a character to fork a process
disconnect netcat
nc localhost 4711
triggers the bug
the sample program will loop with something like
read_connection loop 0x7fad4968a1c0 ev_io 0x18b2650 revents 1 (fd 6)
EAGAIN
Works fine using libev 3.53.
/* triggers epoll spurious notification bug on libev 3.41
Compile using
gcc -Wall -L/opt/libev/lib/libev/ -I/opt/libev/include spurious.c -o spurious -lev
run it
./spurious
in a different terminal
nc localhost 4711
> send a character to fork a process
> disconnect netcat
nc localhost 4711
> triggers the bug
the sample program will loop with something like
read_connection loop 0x7fad4968a1c0 ev_io 0x18b2650 revents 1 (fd 6)
EAGAIN
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <ev.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
struct ev_loop *loop;
void child_watch(EV_P_ struct ev_child *w, int revents)
{
printf("%s loop %p ev_child %p revents %i (pid %i)\n", __PRETTY_FUNCTION__, EV_A_ w, revents, w->pid);
int status;
if (waitpid(w->pid, &status, WNOHANG) != 0)
{
printf("catching dying child %i failed\n", w->pid);
}
ev_child_stop(EV_A_ w);
}
void read_connection(EV_P_ struct ev_io *w, int revents)
{
printf("%s loop %p ev_io %p revents %i (fd %i)\n", __PRETTY_FUNCTION__, EV_A_ w, revents, w->fd);
int size, buf_size = 1024;
char buf[1024];
size = recv(w->fd, buf, buf_size, 0);
if(size <= 0)
{
if( size == -1 && errno == EAGAIN )
{
printf("\t EAGAIN\n");
}else
{
ev_io_stop(loop, w);
close(w->fd);
free(w);
printf("\t -> closed connection\n");
}
return;
}
pid_t p = fork();
if(p == 0)
{ // child
printf("child alive\n");
system("/bin/sh -c \"sleep 10; echo foo;\"");
exit(EXIT_SUCCESS);
}else
{ // parent
printf("parent alive (%i) \n", p);
struct ev_child *child = malloc(sizeof(struct ev_child));
ev_child_init(child, child_watch, p, 0);
ev_child_start(loop, child);
}
}
void accept_connection(EV_P_ struct ev_io *w, int revents)
{
printf("%s loop %p ev_io %p revents %i (fd %i)\n", __PRETTY_FUNCTION__, EV_A_ w, revents, w->fd);
struct ev_io *io = malloc(sizeof(struct ev_io));
struct sockaddr sa;
socklen_t sizeof_sa = sizeof(sa);
int s = accept(w->fd, &sa, &sizeof_sa);
fcntl(s,F_SETFL,O_NONBLOCK);
ev_io_init(io, read_connection, s, EV_READ);
ev_io_start(loop, io);
}
int main()
{
// loop = ev_default_loop(EVBACKEND_SELECT);
loop = ev_default_loop(EVBACKEND_EPOLL);
int s = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr sa;
struct sockaddr_in *si = (struct sockaddr_in *)&sa;
si->sin_family = AF_INET;
si->sin_port = htons(4711);
si->sin_addr.s_addr = INADDR_ANY;
bind(s, &sa, sizeof(sa));
listen(s, 10);
struct ev_io *io = malloc(sizeof(struct ev_io));
ev_io_init(io, accept_connection, s, EV_READ);
ev_io_start(loop, io);
ev_loop(loop, 0);
return 0;
}
_______________________________________________
libev mailing list
[email protected]
http://lists.schmorp.de/cgi-bin/mailman/listinfo/libev