Received: from PACIFIC-CARRIER-ANNEX.MIT.EDU by po7.MIT.EDU (5.61/4.7) id AA29677; Sat, 9 Mar 96 23:27:51 EST
Received: from kipr.laurel.us.net by MIT.EDU with SMTP
	id AA04200; Sat, 9 Mar 96 23:26:27 EST
Received: (from rsargent@localhost) by gecko.newtonlabs.com (8.6.12/8.6.12) id XAA07682; Sat, 9 Mar 1996 23:32:54 -0500
Date: Sat, 9 Mar 1996 23:32:54 -0500
Message-Id: <199603100432.XAA07682@gecko.newtonlabs.com>
From: Randy Sargent <rsargent@newtonlabs.com>
To: pthreads-bugs@MIT.EDU
Cc: rsargent@gecko.newtonlabs.com
Subject: problem with select


Firstly, thanks to all for all the hard work getting pthreads to the point
it's at now.  It's quite impressive.  I apologize for the following bug
report since I didn't spend the time to see if I could figure out how to
fix it.

I believe I have found two bugs with select.

I am using Linux Slackware 3.0 with a newly compiled 1.3.62 kernel.
I can reproduce the bug with pthreads-1_60_beta4_1 and also with what I
copied from /afs/sipb/project/pthreads/stable/src/pthreads an hour ago.

The first problem goes like this: I have two different threads each doing a
select to find out when an fd can be read (different fd for each thread).
The selects have infinite (NULL) timeout.  

Bug 1:

When I send a character to the fd owned by one of the threads, both selects
return.  The select in each thread indicates by return value that its
respective fd is now readable.  (This is correct for the thread I sent the
character to, incorrect for the thread I didn't send to).  The fd_sets do
correctly reflect the state of things.

Bug 2:

The second bug I am reporting might just be a difference in semantics of
the return value of select.  I expect the value to be equal to the number
of fd's set in the fd_sets.  Instead, it seems to be equal to the number I
pass to select in "max_fd", except in the case that the select exits
immediately, in which case it returns the correct value.  Looking at the
code I see two different cases.  The first, where exit is immediate,
returns the value given by the system's "select" call, while the second,
requiring sleeping the task until the fd is readable, seems to have the
bug.


Finally, for some reason I can't send ctrl-c (SIGINT) my program to kill
it.  Any ideas?

-- Randy Sargent

To reproduce the bug:
% pgcc test1.c -o test1
% test1

Two xterms will be created (I use the -S option, which I haven't tested on
platforms other than Linux).  Both are created at +0+0, so you'll have to
move one to see the other.  Ignore the hex characters in the display at
first (these show the X windows ID of the xterm, a side-effect of the -S).

Now type at each of the xterms.  Any time you see '!!!' you've found a
bug.  When you alternate between windows you should see 'bug 1'.  'bug 2'
happens just about all the time.

(The output can be slightly confusing because after a failed select, the
thread will do a "read" call anyway, which now blocks.)

-------------------------------------------------
                  test1.c
-------------------------------------------------

#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int make_window(void)
{
  int fds[2];
  if (pipe(fds) < 0) {
    perror("fork");
    exit(-1);
  }
  if (!fork()) {
    /* I am the child */
    char geom[100], ptyspec[100];
    sprintf(ptyspec, "-Saa%d", fds[1]);
    execlp("xterm", "xterm", "-geometry", "+0+0", ptyspec, NULL);
    /* should never reach here */
  }
  return fds[0];
}

void *test_select(void *arg)
{
  int win= *(int*)arg;

  while (1) {
    char c;
    int nfound;
    fd_set read_set, write_set, exceptional_set;
    
    FD_ZERO(&read_set);
    /*FD_ZERO(&write_set);
    FD_ZERO(&exceptional_set);*/
    
    FD_SET(win, &read_set);
    
    printf("read_set before call %lx\n", *(long*)&read_set);

    /* This is a select with no timeout, so should always finish
       with nfound=1 and FD_ISSET(win, &read_set) true */
    
    /*nfound= select(win+1, &read_set, &write_set, &exceptional_set, NULL);*/
    nfound= select(win+1, &read_set, NULL, NULL, NULL);

    printf("fd %d: nfound=%d", win, nfound);
    if (nfound != 1) printf("!!!"); /* FOUND BUG */
    
    printf("  FD_ISSET(win, &read_set)=%d",
	   FD_ISSET(win, &read_set));
    if (!FD_ISSET(win, &read_set)) printf("!!!"); /* FOUND BUG */
    
    printf("\n");
    
    printf("read_set after call %lx\n", *(long*)&read_set);
    
    if (1 == read(win, &c, 1)) {
      printf("Got `%c' from fd %d\n", c, win);
    } else {
      printf("Didn't get read\n");
    }
    fflush(stdout);
  }
}  

int try_second_window= 1;
  
void main()
{
  pthread_t thread1;
  int win1, win2;

  win1= make_window();
  if (try_second_window) win2= make_window();

  pthread_create(&thread1, NULL, test_select, &win1);

  if (try_second_window) {
    test_select(&win2);
  } else {
    while(1);
  }
  
}
