2016-11-28 10 views
0

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_INEVFILT_READ)を通過し、それに続くコールでCURL_POLL_OUTEVFILT_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の動作の誤解ですか?

答えて

0

ここで問題となるのは、EVFILT_READフラグとEVFILT_WRITEフラグを 'または'一緒に使用できないことです。

複数のフィルタを有効または無効にする場合は、複数のevset構造でEV_SET()を複数回呼び出す必要があります。上記の例では

非機能コード:

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) 

行った後、この変更のすべてが働きました。

関連する問題