Re: (boost::)asio and kqueue problem

From: Adrian Chadd <adrian.chadd_at_gmail.com>
Date: Tue, 19 Jul 2016 10:02:08 -0700
heh, nice catch. Would you please file a PR so we don't forget?

Thanks!


-a


On 19 July 2016 at 08:35, Hartmut Brandt <hartmut.brandt_at_dlr.de> wrote:
> Hi,
>
> I'm trying to use asio (that's boost::asio without boost) to handle
> listening sockets asynchronuosly. This appears not to work. There are also
> some reports on the net about this problem. I was able to reproduce the
> problem with a small C-programm that does the same steps as asio. The
> relevant sequence of system calls is:
>
> kqueue()                                         = 3 (0x3)
> socket(PF_INET,SOCK_STREAM,6)                    = 4 (0x4)
> setsockopt(0x4,0xffff,0x800,0x7fffffffea2c,0x4)  = 0 (0x0)
> kevent(3,{ 4,EVFILT_READ,EV_ADD|EV_CLEAR,0x0,0x0,0x0
> 4,EVFILT_WRITE,EV_ADD|EV_CLEAR,0x0,0x0,0x0 },2,0x0,0,0x0) = 0 (0x0)
> setsockopt(0x4,0xffff,0x4,0x7fffffffea2c,0x4)    = 0 (0x0)
> bind(4,{ AF_INET 0.0.0.0:8080 },16)              = 0 (0x0)
> listen(0x4,0x80)                                 = 0 (0x0)
> ioctl(4,FIONBIO,0xffffea2c)                      = 0 (0x0)
> kevent(3,{ 4,EVFILT_READ,EV_ADD|EV_CLEAR,0x0,0x0,0x0
> 4,EVFILT_WRITE,EV_ADD|EV_CLEAR,0x0,0x0,0x0 },2,0x0,0,0x0) = 0 (0x0)
> kevent(3,0x0,0,0x7fffffffe5a0,32,0x0)            ERR#4 'Interrupted system
> call'
>
> The problem here is that asio registers each file descriptor with
> EVFILT_READ and EVFILT_WRITE as soon as it is opened (first kevent call).
> After bringing the socket into the listening state and when async_accept()
> is called it registers the socket a second time. According to the man page
> this is perfectly legal and can be used to modify the registration.
>
> With this sequence of calls kevent() does not return when a connection is
> established successfully.
>
> I tracked down the problem and the reason is in soo_kqfilter(). This is
> called for the first EVFILT_READ registration and decides based on the
> SO_ACCEPTCONN flag which filter operations to use solisten_filtops or
> soread_filtops. In this case it chooses soread_filtops.
>
> The second EVFILT_READ registration does not call soo_kqfilter() again, but
> just updates the filter from the data and fflags field so the listening
> socket ends up with the wrong filter operations.
>
> The attached patch fixes this (kind of) by using the f_touch operation
> (currently used only by the user filter). The filt_sotouch() function
> changes the operation pointer in the knote when the socket is now in the
> listening state. I suppose that the required locking is already done in
> kqueue_register(), but I'm not sure. Asynchronous accepting now works.
>
> A better fix would probably be to change the operation vector on all knotes
> attached to the socket in solisten(), but I fear I don't have the necessary
> understanding of the locking that is required for this.
>
> Could somebody with enough kqueue() knowledge look whether the patch is
> correct lock-wise?
>
> Regards,
> harti
>
> Index: kern_event.c
> ===================================================================
> --- kern_event.c        (revision 302977)
> +++ kern_event.c        (working copy)
> _at__at_ -1350,8 +1350,8 _at__at_
>         KQ_UNLOCK(kq);
>         knl = kn_list_lock(kn);
>         kn->kn_kevent.udata = kev->udata;
> -       if (!fops->f_isfd && fops->f_touch != NULL) {
> -               fops->f_touch(kn, kev, EVENT_REGISTER);
> +       if (kn->kn_fop->f_touch != NULL) {
> +               kn->kn_fop->f_touch(kn, kev, EVENT_REGISTER);
>         } else {
>                 kn->kn_sfflags = kev->fflags;
>                 kn->kn_sdata = kev->data;
> Index: uipc_socket.c
> ===================================================================
> --- uipc_socket.c       (revision 302977)
> +++ uipc_socket.c       (working copy)
> _at__at_ -160,6 +160,7 _at__at_
>  static void    filt_sowdetach(struct knote *kn);
>  static int     filt_sowrite(struct knote *kn, long hint);
>  static int     filt_solisten(struct knote *kn, long hint);
> +static void    filt_sotouch(struct knote *kn, struct kevent *kev, u_long
> type);
>  static int inline hhook_run_socket(struct socket *so, void *hctx, int32_t
> h_id);
>  fo_kqfilter_t  soo_kqfilter;
>
> _at__at_ -172,6 +173,7 _at__at_
>         .f_isfd = 1,
>         .f_detach = filt_sordetach,
>         .f_event = filt_soread,
> +       .f_touch = filt_sotouch,
>  };
>  static struct filterops sowrite_filtops = {
>         .f_isfd = 1,
> _at__at_ -3091,6 +3093,31 _at__at_
>         return (0);
>  }
>
> +static void
> +filt_sotouch(struct knote *kn, struct kevent *kev, u_long type)
> +{
> +       struct socket *so = kn->kn_fp->f_data;
> +
> +       switch (type) {
> +       case EVENT_REGISTER:
> +               if (kn->kn_fop == &soread_filtops &&
> +                   (so->so_options & SO_ACCEPTCONN))
> +                       kn->kn_fop = &solisten_filtops;
> +
> +               kn->kn_sfflags = kev->fflags;
> +               kn->kn_sdata = kev->data;
> +               break;
> +
> +        case EVENT_PROCESS:
> +               *kev = kn->kn_kevent;
> +               break;
> +
> +       default:
> +               panic("filt_sotouch() - invalid type (%ld)", type);
> +               break;
> +       }
> +}
> +
>  /*
>   * Some routines that return EOPNOTSUPP for entry points that are not
>   * supported by a protocol.  Fill in as needed.
> _______________________________________________
> freebsd-current_at_freebsd.org mailing list
> https://lists.freebsd.org/mailman/listinfo/freebsd-current
> To unsubscribe, send any mail to "freebsd-current-unsubscribe_at_freebsd.org"
Received on Tue Jul 19 2016 - 15:02:10 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:41:06 UTC