Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 10 Sep 1998 12:54:13 -0400
From:      "Daniel M. Eischen" <eischen@vigrid.com>
To:        info@highwind.com
Cc:        freebsd-current@FreeBSD.ORG, tlambert@primenet.com, eischen@vigrid.com
Subject:   Re: Thread Problems
Message-ID:  <35F80435.41C67EA6@vigrid.com>

next in thread | raw e-mail | index | archive | help
> Okay, I coded up a test program that ALWAYS reproduces this accept()
> blocking problem we have been talking about.
> 
> Check it out:
> 
> % uname -a
> FreeBSD zonda.highwind.com 3.0-19980831-SNAP FreeBSD 3.0-19980831-SNAP #0: Mon Aug 31 14:03:19 GMT 1998     root@make.ican.net:/usr/src/sys/compile/GENERIC  i386
> 
> My libc_r is EXTREMELY up-to-date. ~2 days old.
> 
> The program does the following:
> 
> 1. spawns an a thread to loop on "accept"
> 2. fork/exec's a child talking down a socket pair
> 3. loops in main()
> 

It looks like the problem lies in the threads implementation
of fork and execve.  When you fork, the child process gets a
copy of the parents file descriptors, which includes the listen
socket.  When you exec, the threads library goes through its
list of file descriptors and returns their mode to non-blocking
(unless the application explicitly sets the mode to non-blocking).

You can see this in uthread_execve.c:

	/*
	 * Enter a loop to set all file descriptors to blocking
	 * if they were not created as non-blocking:
	 */
	for (i = 0; i < _thread_dtablesize; i++) {
		/* Check if this file descriptor is in use: */
		if (_thread_fd_table[i] != NULL &&
			!(_thread_fd_table[i]->flags & O_NONBLOCK)) {
			/* Get the current flags: */
			flags = _thread_sys_fcntl(i, F_GETFL, NULL);
			/* Clear the nonblocking file descriptor flag: */
			_thread_sys_fcntl(i, F_SETFL, flags & ~O_NONBLOCK);
		}
	}

I'm not exactly sure how the threads library should be fixed to
Do The Right Thing, though.  How can the threads library know what
files are going to be used?  Perhaps it can flag the file as a socket
when it is created and not clear the non-blocking flag in this case...

A simple solution that you can make in your application, is to close
all relevent sockets after the fork and before the execl.  When you
close the file, it will not affect the non-blocking flag.  Your code
will have to make the sockets visible to the forkExec routine so it
can close them.  Another method is to perform the forkExec before
you create the sockets, although this may not be possible in your
real application.

Dan Eischen
eischen@vigrid.com

***************
*** 16,26 ****
  #include <unistd.h>
  
  unsigned short port = 20000;
  
  void *acceptThread(void *)
  {
      // Create the socket
!     int fd = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
      if (fd == -1) { ::printf("socket failed"); }
  
      // Make it reusable
--- 16,27 ----
  #include <unistd.h>
  
  unsigned short port = 20000;
+ int            fd;
  
  void *acceptThread(void *)
  {
      // Create the socket
!     fd = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
      if (fd == -1) { ::printf("socket failed"); }
  
      // Make it reusable
***************
*** 117,122 ****
--- 118,126 ----
      }
  
      if (!pid) { // In Child
+         // Close the socket
+         ::close (fd);
+ 
          // Enter our own process group (avoids signal nastyness)
          if (::setsid() == -1) {
              ::_exit(EXIT_FAILURE);

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?35F80435.41C67EA6>