I'm running into an issue that I'm not sure if is expected behavior from kqueue or if it's something I'm doing wrong.
I need to install separate events with kqueue for a single socket file descriptor. The separate events are one for (sockfd,EVFILT_READ) and (sockfd,EVFILT_WRITE). However when I pull events out of kqueue later I get two events from kqueue, but both events contain (event.filter & EVFILT_READ) and (event.filter & EVFILT_WRITE).
I need them to be separate otherwise I don't know which event actually is ready, read or write?
Here's some sample code I put together to illustrate the issue. Please keep in mind this is just test code to figure out why I'm getting both filter flags in each event.
int main(int argc, char ** argv) {
//listening_sockfd = setup listening socket
//setup kqueue and vars
int kq = kqueue();
int res = 0;
int bufres = 0;
struct timespec ts = {0};
struct timespec * tsp = &ts;
struct kevent ke2[2];
//install read for listening socket
struct kevent ke1;
bzero(&ke1,sizeof(ke1));
ke1.ident = listening_sockfd;
ke1.flags = EV_ADD;
ke1.filter = EVFILT_READ;
res = kevent(kq,&ke1,1,NULL,0,tsp);
while(1) {
new_client:
//wait for a client to connect
res = kevent(kq,NULL,0,&ke1,1,NULL);
if(res < 0) printf("%s\n", strerror(errno));
if(res > 0) {
//accept the client
int clientfd = accept(skinfo.sockfd,NULL,NULL);
//install read events for client
bzero(&ke1,sizeof(ke1));
ke1.ident = clientfd;
ke1.flags = EV_ADD;
ke1.filter = EVFILT_READ;
res = kevent(kq,&ke1,1,NULL,0,tsp);
if(res < 0) printf("%s\n",strerror(errno));
while(1) {
//wait for readable content from the client
bzero(&ke1,sizeof(ke1));
res = kevent(kq,NULL,0,&ke1,1,NULL);
if(res < 0) printf("%s\n",strerror(errno));
if(res > 0) {
//check for client disconnect
if(ke1.flags & EV_EOF) {
close(clientfd);
goto new_client;
}
//now install write events for client
bzero(&ke1,sizeof(ke1));
ke1.ident = clientfd;
ke1.flags = EV_ADD;
ke1.filter = EVFILT_WRITE;
res = kevent(kq,&ke1,1,NULL,0,tsp);
if(res < 0) printf("%s\n",strerror(errno));
//now wait for it to be writable - this will return
//immediately because the socket is writable.
res = kevent(kq,NULL,0,ke2,2,NULL);
if(res < 0) printf("%s\n",strerror(errno));
if(res >= 0) {
char buf[128];
printf("res: %i\n",res);
//we have two events from kqueue because I installed
//two (ident,filter) pairs.
int i = 0;
for(i; i<2; i++) {
printf("ident: %i\n",ke2[i].ident);
printf("filter[%i] %lu\n",i,ke2[i].filter);
printf("data: %lu\n",ke2[i].data);
//QUESTION: Why does EVFILT_READ && EVFILT_WRITE come
//back in the same event when I installed two seperate
//(ident,filter) pairs?
if(ke2[i].filter & EVFILT_READ) printf("EVFILT_READ\n");
if(ke2[i].filter & EVFILT_WRITE) printf("EVFILT_WRITE\n");
if(ke2[i].filter & EVFILT_READ) {
printf("readable!\n");
bufres = read(clientfd,buf,128);
}
if(ke2[i].filter & EVFILT_WRITE) {
printf("writable\n");
//shut off write events to stop infinite loop
//because the socket is writable
bzero(&ke1,sizeof(ke1));
ke1.ident = clientfd;
ke1.flags = EV_DELETE;
ke1.filter = EVFILT_WRITE;
res = kevent(kq,&ke1,1,NULL,0,tsp);
write(clientfd,buf,bufres);
}
}
}
}
}
}
}
}
I haven't been able to figure this out. Why does each kqueue event contain both EVFILT_READ and EVFILT_WRITE when I installed separate events?
The real issue this is creating for me is that because these events always report EVFILT_READ, it has the side-effects of the code always thinking there's a read available, but there's really not, so a call to read() will be unsuccessful and have other consequences. Note that this code doesn't show those consequences, this is just a sample of the issue I wrote so I figure this out and continue with my real code.
Any ideas?
PS Here's output from the printf:
res: 2
ident: 5
filter[0] 4294967295
data: 5
EVFILT_READ
EVFILT_WRITE
readable!
writable
ident: 5
filter[1] 4294967294
data: 146988
EVFILT_READ
EVFILT_WRITE
readable!
writable
AH! I think I figured it out. What kqueue documentation doesn't say is that event.filter doesn't contain bit flags. So I need to check for read/write with event.filter == EVFILT_READ, event.filter == EVFILT_WRITE.
As you noticed, they are not bits to be tested.
Consulting the header on OS X, we find
#define EVFILT_READ (-1)
#define EVFILT_WRITE (-2)
Since -1
is all 1-bits, x & EVFILT_READ
will be true for any nonzero x
, and x & EVFILT_WRITE
will be true for x
not equal to zero or one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With