It was attached to my mail, but maybe got removed somewhere. Here it is. It 
does not use asio, but reproduces the same sequence of system calls. You start 
it and the try to connect with telnet to port 10000.

harti

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/filio.h>

#include <sys/ioctl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <err.h>

static void
wait_loop(int kq, int sock)
{
        struct kevent ev[32];
        struct sockaddr_in addr;
        socklen_t socklen;

        for (;;) {
                int nev = kevent(kq, NULL, 0, ev, 32, NULL);
                if (nev < 1)
                        err(1, "kevent");
                for (int i = 0; i < nev; ++i) {
                        if (ev[i].ident == sock) {
                                printf("accept\n");
                                int fd = accept(ev[i].ident,
                                    (struct sockaddr *)&addr, &socklen);
                                if (fd == -1)
                                        err(1, "accept");
                        }
                }
        }
}

int
main()
{
        struct sockaddr_in addr;

        /* open a TCP socket */
        int kq = kqueue();

        int sock = socket(PF_INET, SOCK_STREAM, 0);

        struct kevent ev[2];
        EV_SET(&ev[0], sock, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, NULL);
        EV_SET(&ev[1], sock, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, NULL);

        int opt = 1;
        setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));

        if (kevent(kq, ev, 2, NULL, 0, NULL) == -1)
            err(1, "kevent");

        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

        memset(&addr, 0, sizeof(addr));
        addr.sin_port = htons(10000);

        bind(sock, (struct sockaddr *)&addr, sizeof(addr));
        listen(sock, 0x80);

        ioctl(sock, FIONBIO, &opt);

        if (kevent(kq, ev, 2, NULL, 0, NULL) == -1)
                err(1, "kevent");

        wait_loop(kq, sock);
}



-----Original Message-----
From: Gleb Smirnoff [mailto:gleb...@freebsd.org] 
Sent: Thursday, February 02, 2017 8:24 PM
To: Brandt, Hartmut
Cc: src-committ...@freebsd.org; svn-src-all@freebsd.org; 
svn-src-h...@freebsd.org
Subject: Re: svn commit: r313043 - head/sys/kern

  Hartmut,

  Thanks for explanation! Is there a test program available to reproduce the 
problem? I want to try the sequence on my branch.

On Thu, Feb 02, 2017 at 08:29:20AM +0000, hartmut.bra...@dlr.de wrote:
H> To be honest - I feared that when I saw your messages regarding this. Here 
is my original message from july. Attached is also a small test program.
H> 
H> Hi,
H> 
H> 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:
H> 
H> kqueue()                                      = 3 (0x3)
H> socket(PF_INET,SOCK_STREAM,6)                         = 4 (0x4)
H> setsockopt(0x4,0xffff,0x800,0x7fffffffea2c,0x4)       = 0 (0x0)
H> 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)
H> setsockopt(0x4,0xffff,0x4,0x7fffffffea2c,0x4)         = 0 (0x0)
H> bind(4,{ AF_INET 0.0.0.0:8080 },16)           = 0 (0x0)
H> listen(0x4,0x80)                              = 0 (0x0)
H> ioctl(4,FIONBIO,0xffffea2c)                   = 0 (0x0)
H> 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)
H> kevent(3,0x0,0,0x7fffffffe5a0,32,0x0)                 ERR#4 'Interrupted 
system call'
H> 
H> 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). 
H> 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.
H> 
H> With this sequence of calls kevent() does not return when a connection is 
established successfully.
H> 
H> 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.
H> 
H> 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.
H> 
H> 
H> 
H> -----Original Message-----
H> From: Gleb Smirnoff [mailto:gleb...@freebsd.org]
H> Sent: Wednesday, February 01, 2017 7:08 PM
H> To: Hartmut Brandt
H> Cc: src-committ...@freebsd.org; svn-src-all@freebsd.org; 
H> svn-src-h...@freebsd.org
H> Subject: Re: svn commit: r313043 - head/sys/kern
H> 
H> On Wed, Feb 01, 2017 at 01:12:07PM +0000, Hartmut Brandt wrote:
H> H> Author: harti
H> H> Date: Wed Feb  1 13:12:07 2017
H> H> New Revision: 313043
H> H> URL: https://svnweb.freebsd.org/changeset/base/313043
H> H> 
H> H> Log:
H> H>   Merge filt_soread and filt_solisten and decide what to do when checking
H> H>   for EVFILT_READ at the point of the check not when the event is 
registers.
H> H>   This fixes a problem with asio when accepting a connection.
H> H>   
H> H>   Reviewed by:    kib@, Scott Mitchell
H> 
H> This goes into opposite direction with what I am doing:
H> 
H> https://reviews.freebsd.org/D9356
H> 
H> Can you please explain the problem with asio when accepting a connection?
H> 
H> --
H> Totus tuus, Glebius.
H> 

H> #include <sys/socket.h>
H> #include <sys/types.h>
H> #include <sys/event.h>
H> #include <sys/filio.h>
H> 
H> #include <sys/ioctl.h>
H> #include <netinet/in.h>
H> #include <stdio.h>
H> #include <string.h>
H> #include <err.h>
H> 
H> static void
H> wait_loop(int kq, int sock)
H> {
H>      struct kevent ev[32];
H>      struct sockaddr_in addr;
H>      socklen_t socklen;
H> 
H>      for (;;) {
H>              int nev = kevent(kq, NULL, 0, ev, 32, NULL);
H>              if (nev < 1)
H>                      err(1, "kevent");
H>              for (int i = 0; i < nev; ++i) {
H>                      if (ev[i].ident == sock) {
H>                              printf("accept\n");
H>                              int fd = accept(ev[i].ident,
H>                                  (struct sockaddr *)&addr, &socklen);
H>                              if (fd == -1)
H>                                      err(1, "accept");
H>                      }
H>              }
H>      }
H> }
H> 
H> int
H> main()
H> {
H>      struct sockaddr_in addr;
H> 
H>      /* open a TCP socket */
H>      int kq = kqueue();
H> 
H>      int sock = socket(PF_INET, SOCK_STREAM, 0);
H> 
H>      struct kevent ev[2];
H>      EV_SET(&ev[0], sock, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, NULL);
H>      EV_SET(&ev[1], sock, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, NULL);
H> 
H>      int opt = 1;
H>      setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));
H> 
H>      if (kevent(kq, ev, 2, NULL, 0, NULL) == -1)
H>          err(1, "kevent");
H> 
H>      setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
H> 
H>      memset(&addr, 0, sizeof(addr));
H>      addr.sin_port = htons(10000);
H> 
H>      bind(sock, (struct sockaddr *)&addr, sizeof(addr));
H>      listen(sock, 0x80);
H> 
H>      ioctl(sock, FIONBIO, &opt);
H> 
H>      if (kevent(kq, ev, 2, NULL, 0, NULL) == -1)
H>              err(1, "kevent");
H> 
H>      wait_loop(kq, sock);
H> }


-- 
Totus tuus, Glebius.
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/filio.h>

#include <sys/ioctl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <err.h>

static void
wait_loop(int kq, int sock)
{
        struct kevent ev[32];
        struct sockaddr_in addr;
        socklen_t socklen;

        for (;;) {
                int nev = kevent(kq, NULL, 0, ev, 32, NULL);
                if (nev < 1)
                        err(1, "kevent");
                for (int i = 0; i < nev; ++i) {
                        if (ev[i].ident == sock) {
                                printf("accept\n");
                                int fd = accept(ev[i].ident,
                                    (struct sockaddr *)&addr, &socklen);
                                if (fd == -1)
                                        err(1, "accept");
                        }
                }
        }
}

int
main()
{
        struct sockaddr_in addr;

        /* open a TCP socket */
        int kq = kqueue();

        int sock = socket(PF_INET, SOCK_STREAM, 0);

        struct kevent ev[2];
        EV_SET(&ev[0], sock, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, NULL);
        EV_SET(&ev[1], sock, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, NULL);

        int opt = 1;
        setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));

        if (kevent(kq, ev, 2, NULL, 0, NULL) == -1)
            err(1, "kevent");

        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

        memset(&addr, 0, sizeof(addr));
        addr.sin_port = htons(10000);

        bind(sock, (struct sockaddr *)&addr, sizeof(addr));
        listen(sock, 0x80);

        ioctl(sock, FIONBIO, &opt);

        if (kevent(kq, ev, 2, NULL, 0, NULL) == -1)
                err(1, "kevent");

        wait_loop(kq, sock);
}
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to