kqueueに基づいて非同期入出力イベントループにcurlを統合しました。libcurl macOS上のkeventとの非同期統合Sierra
libcurlには、アプリケーションイベントループに統合するための優れたAPIがあります。
libcurlには2つのコールバック、1つはタイマー(要求/接続時間の制限に使用)を設定するコールバック、もう1つは読み込み/書き込み/エラーイベントのlibcurlのファイル記述子を登録するコールバックです。
FD登録を実行するために使用されるコールバックのドキュメントはここにある:に興味があるのlibcurl何かのイベントのコールバックを通知CURLMOPT_SOCKETFUNCTION
引数は、4つの列挙型の値があります。
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は古いフィルタを削除していないようです(私たちは古いフィルタを引き続き受け取ります)。
2つの別々の呼び出しで2つの操作を適用すると、それは完全に機能します。
これはSierraのkevent実装のバグですか、keventの動作の誤解ですか?