Subject: Re: 1.5.9-1: socket() appears NOT to be thread-safe
From: "Enzo Michelangeli" <[email-masked]>
To: <[email-masked]>
Cc: "\"Brian Ford\"" <[email-masked]>
Date: Thu, 15 Apr 2004 11:16:03 +0800

----- Original Message ----- 
From: "Brian Ford" <[email-masked]>
To: "Enzo Michelangeli" <[email-masked]>
Cc: <[email-masked]>
Sent: Thursday, April 15, 2004 1:05 AM
Subject: Re: 1.5.9-1: socket() appears NOT to be thread-safe

> On Wed, 14 Apr 2004, Enzo Michelangeli wrote:
>
> > While porting to Cygwin the Linux application "sipd"
> > (http://www.sxdesign.com/index.php?page=developer&submnu=sipd ),
> > which uses pthreads, I noticed that socket() calls issued
> > concurrently by several threads often failed, with
> > strerror(errno) saying "operation not permitted". Once I
> > protected all the calls with mutex locks, such errors
> > went away. Is Cygwin's implementation of socket() known to be
> > thread-unsafe?
>
> FWIW, I have seen this symptom as well, primarily on a dual CPU,
> Hyperthreaded XP box under 1.5.[8|9]+.  In fact, I just confirmed
> it still exists in a current CVS build from this morning.
>
> It seems like a race condition.  Running under strace fixes it,
> so it has been difficult to isolate.  If I have time, I'll try
> to look at it again soon.  Would you like to work on it together?

What I can do for the time being is to provide the testcase that Corinna
asked for (attached below). Here are the results:

em@emnb ~/unsafesocket
$ cc -o unsafesocket unsafesocket.c

em@emnb ~/unsafesocket
$ ./unsafesocket
usage: ./unsafesocket [n|y]

em@emnb ~/unsafesocket
$ ./unsafesocket n
NOT protecting socket() call with mutex:
Thread 0: socket() returned 3, strerror(errno)="No error"
Thread 1: socket() returned -1, strerror(errno)="Operation not permitted"
Thread 2: socket() returned -1, strerror(errno)="Operation not permitted"
Thread 3: socket() returned 3, strerror(errno)="No error"

em@emnb ~/unsafesocket
$ ./unsafesocket n
NOT protecting socket() call with mutex:
Thread 0: socket() returned -1, strerror(errno)="Operation not permitted"
Thread 1: socket() returned 3, strerror(errno)="No error"
Thread 2: socket() returned -1, strerror(errno)="Operation not permitted"
Thread 3: socket() returned -1, strerror(errno)="Operation not permitted"

em@emnb ~/unsafesocket
$ ./unsafesocket y
Protecting socket() call with mutex:
Thread 0: socket() returned 3, strerror(errno)="No error"
Thread 1: socket() returned 4, strerror(errno)="No error"
Thread 2: socket() returned 5, strerror(errno)="No error"
Thread 3: socket() returned 6, strerror(errno)="No error"

em@emnb ~/unsafesocket
$

By the way, even in case of no error the socket calls return the same
value of fd. Is this OK??

Cheers --

Enzo

--------- begin unsafesocket.c ---------

#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdio.h>
#include <pthread.h>

#define MAXTHREADS 4

static int sync_with_mutex = 0;

struct thrdata {
 int thrnum;
 pthread_t t;
 int socket_fd;
 int socket_errno;
} th[MAXTHREADS];

static void *thread_code(void *p);

main(int argc, char *argv[]) {
 int i;

 if(argc == 1) {
  printf("usage: %s [n|y]\n", argv[0]);
  exit(1);
 }

 if(argv[1][0] == 'y' || argv[1][0] == 'Y') {
  printf("Protecting socket() call with mutex:\n");
  sync_with_mutex = 1;
 } else {
  printf("NOT protecting socket() call with mutex:\n");
  sync_with_mutex = 0;
 }

 for(i=0; i<MAXTHREADS; i++)
 {
  th[i].thrnum = i;
  pthread_create(&th[i].t, NULL, thread_code, (void *)&(th[i]));
 }

 for(i=0; i<MAXTHREADS; i++) {
  pthread_join(th[i].t, NULL);
 }

 for(i=0; i<MAXTHREADS; i++) {
  printf("Thread %d: socket() returned %d, strerror(errno)=\"%s\"\n",
    i, th[i].socket_fd, strerror(th[i].socket_errno));

  if(th[i].socket_fd > 0)
   close(th[i].socket_fd);
 }

 exit(0);
}

static void *thread_code(void *p) {
 struct thrdata *pt = (struct thrdata *)p;
 int fd;
 struct sockaddr_in sa;
 static pthread_mutex_t __mutex = PTHREAD_MUTEX_INITIALIZER;

 bzero(&(sa), sizeof(sa));
 sa.sin_family = AF_INET;
 sa.sin_addr.s_addr = htonl(INADDR_ANY);
 sa.sin_port = 1024 + pt->thrnum;

 if(sync_with_mutex) {
  pthread_mutex_lock(&__mutex); /* begin critical area */
 }

 fd = socket(AF_INET, SOCK_DGRAM, 0);

 if(sync_with_mutex) {
  pthread_mutex_unlock(&__mutex); /* begin critical area */
 }

 /* report results to main thread */
 pt->socket_fd = fd;
 pt->socket_errno = errno;

 return(NULL);
}

--------- end unsafesocket.c ---------

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/