Skip to content
  • Ulrich Drepper's avatar
    flag parameters: NONBLOCK in socket and socketpair · 77d27200
    Ulrich Drepper authored
    
    
    This patch introduces support for the SOCK_NONBLOCK flag in socket,
    socketpair, and  paccept.  To do this the internal function sock_attach_fd
    gets an additional parameter which it uses to set the appropriate flag for
    the file descriptor.
    
    Given that in modern, scalable programs almost all socket connections are
    non-blocking and the minimal additional cost for the new functionality
    I see no reason not to add this code.
    
    The following test must be adjusted for architectures other than x86 and
    x86-64 and in case the syscall numbers changed.
    
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #include <fcntl.h>
    #include <pthread.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <sys/syscall.h>
    
    #ifndef __NR_paccept
    # ifdef __x86_64__
    #  define __NR_paccept 288
    # elif defined __i386__
    #  define SYS_PACCEPT 18
    #  define USE_SOCKETCALL 1
    # else
    #  error "need __NR_paccept"
    # endif
    #endif
    
    #ifdef USE_SOCKETCALL
    # define paccept(fd, addr, addrlen, mask, flags) \
      ({ long args[6] = { \
           (long) fd, (long) addr, (long) addrlen, (long) mask, 8, (long) flags }; \
         syscall (__NR_socketcall, SYS_PACCEPT, args); })
    #else
    # define paccept(fd, addr, addrlen, mask, flags) \
      syscall (__NR_paccept, fd, addr, addrlen, mask, 8, flags)
    #endif
    
    #define PORT 57392
    
    #define SOCK_NONBLOCK O_NONBLOCK
    
    static pthread_barrier_t b;
    
    static void *
    tf (void *arg)
    {
      pthread_barrier_wait (&b);
      int s = socket (AF_INET, SOCK_STREAM, 0);
      struct sockaddr_in sin;
      sin.sin_family = AF_INET;
      sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
      sin.sin_port = htons (PORT);
      connect (s, (const struct sockaddr *) &sin, sizeof (sin));
      close (s);
      pthread_barrier_wait (&b);
    
      pthread_barrier_wait (&b);
      s = socket (AF_INET, SOCK_STREAM, 0);
      sin.sin_port = htons (PORT);
      connect (s, (const struct sockaddr *) &sin, sizeof (sin));
      close (s);
      pthread_barrier_wait (&b);
    
      return NULL;
    }
    
    int
    main (void)
    {
      int fd;
      fd = socket (PF_INET, SOCK_STREAM, 0);
      if (fd == -1)
        {
          puts ("socket(0) failed");
          return 1;
        }
      int fl = fcntl (fd, F_GETFL);
      if (fl == -1)
        {
          puts ("fcntl failed");
          return 1;
        }
      if (fl & O_NONBLOCK)
        {
          puts ("socket(0) set non-blocking mode");
          return 1;
        }
      close (fd);
    
      fd = socket (PF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0);
      if (fd == -1)
        {
          puts ("socket(SOCK_NONBLOCK) failed");
          return 1;
        }
      fl = fcntl (fd, F_GETFL);
      if (fl == -1)
        {
          puts ("fcntl failed");
          return 1;
        }
      if ((fl & O_NONBLOCK) == 0)
        {
          puts ("socket(SOCK_NONBLOCK) does not set non-blocking mode");
          return 1;
        }
      close (fd);
    
      int fds[2];
      if (socketpair (PF_UNIX, SOCK_STREAM, 0, fds) == -1)
        {
          puts ("socketpair(0) failed");
          return 1;
        }
      for (int i = 0; i < 2; ++i)
        {
          fl = fcntl (fds[i], F_GETFL);
          if (fl == -1)
            {
              puts ("fcntl failed");
              return 1;
            }
          if (fl & O_NONBLOCK)
            {
              printf ("socketpair(0) set non-blocking mode for fds[%d]\n", i);
              return 1;
            }
          close (fds[i]);
        }
    
      if (socketpair (PF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0, fds) == -1)
        {
          puts ("socketpair(SOCK_NONBLOCK) failed");
          return 1;
        }
      for (int i = 0; i < 2; ++i)
        {
          fl = fcntl (fds[i], F_GETFL);
          if (fl == -1)
            {
              puts ("fcntl failed");
              return 1;
            }
          if ((fl & O_NONBLOCK) == 0)
            {
              printf ("socketpair(SOCK_NONBLOCK) does not set non-blocking mode for fds[%d]\n", i);
              return 1;
            }
          close (fds[i]);
        }
    
      pthread_barrier_init (&b, NULL, 2);
    
      struct sockaddr_in sin;
      pthread_t th;
      if (pthread_create (&th, NULL, tf, NULL) != 0)
        {
          puts ("pthread_create failed");
          return 1;
        }
    
      int s = socket (AF_INET, SOCK_STREAM, 0);
      int reuse = 1;
      setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse));
      sin.sin_family = AF_INET;
      sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
      sin.sin_port = htons (PORT);
      bind (s, (struct sockaddr *) &sin, sizeof (sin));
      listen (s, SOMAXCONN);
    
      pthread_barrier_wait (&b);
    
      int s2 = paccept (s, NULL, 0, NULL, 0);
      if (s2 < 0)
        {
          puts ("paccept(0) failed");
          return 1;
        }
    
      fl = fcntl (s2, F_GETFL);
      if (fl & O_NONBLOCK)
        {
          puts ("paccept(0) set non-blocking mode");
          return 1;
        }
      close (s2);
      close (s);
    
      pthread_barrier_wait (&b);
    
      s = socket (AF_INET, SOCK_STREAM, 0);
      sin.sin_port = htons (PORT);
      setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse));
      bind (s, (struct sockaddr *) &sin, sizeof (sin));
      listen (s, SOMAXCONN);
    
      pthread_barrier_wait (&b);
    
      s2 = paccept (s, NULL, 0, NULL, SOCK_NONBLOCK);
      if (s2 < 0)
        {
          puts ("paccept(SOCK_NONBLOCK) failed");
          return 1;
        }
    
      fl = fcntl (s2, F_GETFL);
      if ((fl & O_NONBLOCK) == 0)
        {
          puts ("paccept(SOCK_NONBLOCK) does not set non-blocking mode");
          return 1;
        }
      close (s2);
      close (s);
    
      pthread_barrier_wait (&b);
      puts ("OK");
    
      return 0;
    }
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    Signed-off-by: default avatarUlrich Drepper <drepper@redhat.com>
    Acked-by: default avatarDavide Libenzi <davidel@xmailserver.org>
    Cc: Michael Kerrisk <mtk.manpages@googlemail.com>
    Cc: "David S. Miller" <davem@davemloft.net>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    77d27200