我正在将curl集成到基于kqueue的异步I/O事件循环中。
libcurl有一个很好的API来集成到应用程序事件循环中。
您可以为libcurl提供两个回调,一个用于设置计时器(用于限制请求/连接时间),另一个用于为读/写/错误事件注册libcurl的文件描述符。
用于执行FD注册的回调文档如下:SOCKETFUNCTION
通知回调libcurl感兴趣的事件的参数有四个枚举值:
CURL_POLL_IN
Wait for incoming data. For the socket to become readable.
CURL_POLL_OUT
Wait for outgoing data. For the socket to become writable.
CURL_POLL_INOUT
Wait for incoming and outgoing data. For the socket to become readable or writable.
CURL_POLL_REMOVE
The specified socket/file descriptor is no longer used by libcurl.
虽然没有显式记录,但是libcurl希望在随后对回调的调用中,事件循环的筛选状态被更新,以匹配它传递的内容。也就是说,如果在第一次调用时它通过了CURL_POLL_IN
(EVFILT_READ
),并且在随后的调用中传递了CURL_POLL_OUT
(EVFILT_WRITE
),那么原始的EVFILT_READ
过滤器就会被删除。
我更新了FD注册代码来处理这个问题。
int fr_event_fd_insert(fr_event_list_t *el, int fd,
fr_event_fd_handler_t read,
fr_event_fd_handler_t write,
fr_event_fd_handler_t error,
void *ctx)
{
int filter = 0;
struct kevent evset[2];
struct kevent *ev_p = evset;
fr_event_fd_t *ef, find;
if (!el) {
fr_strerror_printf("Invalid argument: NULL event list");
return -1;
}
if (!read && !write) {
fr_strerror_printf("Invalid arguments: NULL read and write callbacks");
return -1;
}
if (fd < 0) {
fr_strerror_printf("Invalid arguments: Bad FD %i", fd);
return -1;
}
if (el->exit) {
fr_strerror_printf("Event loop exiting");
return -1;
}
memset(&find, 0, sizeof(find));
/*
* Get the existing fr_event_fd_t if it exists.
*/
find.fd = fd;
ef = rbtree_finddata(el->fds, &find);
if (!ef) {
ef = talloc_zero(el, fr_event_fd_t);
if (!ef) {
fr_strerror_printf("Out of memory");
return -1;
}
talloc_set_destructor(ef, _fr_event_fd_free);
el->num_fds++;
ef->fd = fd;
rbtree_insert(el->fds, ef);
/*
* Existing filters will be overwritten if there's
* a new filter which takes their place. If there
* is no new filter however, we need to delete the
* existing one.
*/
} else {
if (ef->read && !read) filter |= EVFILT_READ;
if (ef->write && !write) filter |= EVFILT_WRITE;
if (filter) {
EV_SET(ev_p++, ef->fd, filter, EV_DELETE, 0, 0, 0);
filter = 0;
}
/*
* I/O handler may delete an event, then
* re-add it. To avoid deleting modified
* events we unset the do_delete flag.
*/
ef->do_delete = false;
}
ef->ctx = ctx;
if (read) {
ef->read = read;
filter |= EVFILT_READ;
}
if (write) {
ef->write = write;
filter |= EVFILT_WRITE;
}
ef->error = error;
EV_SET(ev_p++, fd, filter, EV_ADD | EV_ENABLE, 0, 0, ef);
if (kevent(el->kq, evset, ev_p - evset, NULL, 0, NULL) < 0) {
fr_strerror_printf("Failed inserting event for FD %i: %s", fd, fr_syserror(errno));
talloc_free(ef);
return -1;
}
ef->is_registered = true;
return 0;
}
不幸的是,它不起作用。kevent似乎没有删除旧的过滤器(我们继续接收来自它们的通知)。
更奇怪的是,如果我在两个单独的调用中应用这两个操作,它就会完美地工作。
if (filter) {
EV_SET(&evset, ef->fd, filter, EV_DELETE, 0, 0, 0);
kevent(el->kq, evset, ev_p - evset, NULL, 0, NULL);
filter = 0;
}
这是塞拉事件实现中的一个错误,还是我误解了kevent应该如何工作?
发布于 2017-07-07 23:54:06
这里的问题是,您不能将EVFILT_READ
和EVFILT_WRITE
标志组合在一起。
启用或禁用多个筛选器时,需要在多个EV_SET()
结构上多次调用evset
。
上面示例中的非功能代码:
struct kevent evset[2];
struct kevent *ev_p = evset;
if (read) {
ef->read = read;
filter |= EVFILT_READ;
}
if (write) {
ef->write = write;
filter |= EVFILT_WRITE;
}
ef->error = error;
EV_SET(ev_p++, fd, filter, EV_ADD | EV_ENABLE, 0, 0, ef);
event(el->kq, evset, ev_p - evset, NULL, 0, NULL)
变成:
int count = 0;
struct ev_set[2];
if (read) {
ef->read = read;
EV_SET(ev_set[count++], fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, ef);
}
if (write) {
ef->write = write;
EV_SET(ev_set[count++], fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, ef);
}
ef->error = error;
kevent(el->kq, ev_set, count, NULL, 0, NULL)
在进行此更改之后,一切都按预期进行了工作。
https://stackoverflow.com/questions/40848603
复制相似问题