Bug 119733 - pthread cancelled while pthread cancel state was disabled in a call to writev
Summary: pthread cancelled while pthread cancel state was disabled in a call to writev
Keywords:
Status: CLOSED WORKSFORME
Alias: None
Product: Red Hat Linux
Classification: Retired
Component: glibc
Version: 9
Hardware: i386
OS: Linux
high
medium
Target Milestone: ---
Assignee: Jakub Jelinek
QA Contact: Brian Brock
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2004-04-01 21:13 UTC by oumer
Modified: 2016-11-24 14:57 UTC (History)
2 users (show)

Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Clone Of:
Environment:
Last Closed: 2004-05-27 11:39:35 UTC
Embargoed:


Attachments (Terms of Use)

Description oumer 2004-04-01 21:13:12 UTC
Description of problem:
While a thread's cancel state was disabled, we are able to cancel the
thread. The problem seems to occur when the call to the writev method
is performed. 

Version-Release number of selected component (if applicable):
I am using glibc-2.3.2-27.9 on readhat 9, kernel version 2.4.20-18.9

How reproducible:


Steps to Reproduce:
1.Run the server program first in a terminal (give it the port number)
2.run the client in another terminal with an ip_address of the server
and port number (ip address in string as "123.456.789.101" without the
quotations) 
(Just ignore the server program, it does almost nothing, it is
provided so that the client would be able to run)

The client will first spawn one thread and sleeps for 10 secs and then
it cancelles the thread and exits. The thread, when it starts will
make the connection to the server, set up a cacnellation cleanup
function and then it will enter a loop where it will
infintely calls "dump" function with "false" argument. In the dump
method,  we disable cancellability and then sleep for some amount of
time, write a counter value to the socket using writev method. And
then we enable cancellation and return.

When the 10 sec sleeping main thread wakes up, it tries to cancel the
thread. My expectation was if the thread was inside the loop in the
dump method, it will wait till it finishes that loop (till the point
where the cancellability is enabled again) and will call the cleanup
method, where dump will be called with true argument and we simply
dump the rest of the counter values.

To my surprise, in cases where I tested the code, the thread actually
is cancelled inside the loop where the cancellability was disabled, so
I get an output of something like (I have redirected the output to the
file and shown you the most important part)

please adjust the sleep value and the counter so that the cancelation
will be called before counter reaches the specified value. In my
system with these setting cancellation occurs mostly at when counter
is around 80-100 

Actual results:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
begin:ENABLED to DISABLED
Logger flushing_1: 88 1080853301.442614
Stopping TH1
Logger flushing_2: 88 1080853301.442614
Logger flushing_3: 88 1080853301.442614
Logger flushing_4: 88 1080853301.442614
before:DISABLED to DISABLED
cleanup_1
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
begin:DISABLED to DISABLED ERROR!!
AHA we are gonna be done soon 
Logger flushing_1: 89 1080853301.557398
Logger flushing_2: 89 1080853301.557398
Logger flushing_3: 89 1080853301.557398
Logger flushing_4: 89 1080853301.557398
before:DISABLED to DISABLED
after:DISABLED to DISABLED
Logger flushing_5: 89 1080853301.557398
Logger flushing_1: 90 1080853301.557427
.
.
Expected results:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
begin:ENABLED to DISABLED
Logger flushing_1: 88 1080853301.442614
Stopping TH1
Logger flushing_2: 88 1080853301.442614
Logger flushing_3: 88 1080853301.442614
Logger flushing_4: 88 1080853301.442614
before:DISABLED to DISABLED
after:DISABLED to DISABLED
Logger flushing_5: 88 1080853301.442614
end:DISABLED to ENABLED
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
cleanup_1
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
begin:ENABLED to DISABLED
AHA we are gonna be done soon
Logger flushing_1: 89 1080853301.557398
Logger flushing_2: 89 1080853301.557398
Logger flushing_3: 89 1080853301.557398
Logger flushing_4: 89 1080853301.557398
before:DISABLED to DISABLED
after:DISABLED to DISABLED
Logger flushing_5: 89 1080853301.557398
Logger flushing_1: 90 1080853301.557427
.
.
Additional info:

The problem seems to disappear when I use write instead of writev.
First I doubted that may be the cancelability was changed inside a
call to writev, but it seems it hasn't as I modified the program to
print out values of the cancelability state before and after the call
to writev. Also I tested the code on a redhat 8.0 with glibc 2.2.93-5
and kernel version 2.4.18-14 and the code seems to work ok even when
using writev.

And the source code for the client and server follows
I compiled both client and server using the following script

#!/bin/bash
g++ -g -Wall -Wunused  -D_SVID_SOURCE -DNETFILTER_VERSION=\"1.2.7\"
-D_POSIX_C_SOURCE=199056 -D_
REENTRANT \
-rdynamic -o $1 $1.c /usr/local/lib/iptables.o
/usr/local/lib/libiptc.a -ldl -lipq -lpthread

//client
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <iostream>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>

using namespace std;
pthread_t thread1;

int counter=0;
int sockfd;
int port;
char ip_address[20];
void dump(bool last)
{
  struct timeval ts;

  char cmd[] = "message\n";
  char num[10];
 
  int state;
  printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
  if (pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state)!=0)
    {
      fprintf(stderr, "error setting cancel state to disabled\n");
      abort();
    }
  else
    {
      flockfile(stdout);
      if (state == PTHREAD_CANCEL_ENABLE)
     {
       printf("begin:ENABLED to DISABLED\n");
     }
      else
     {
       printf("begin:DISABLED to DISABLED ERROR!! \n");
     }
     
      funlockfile(stdout);
    }


 
  if (last)
   {
      flockfile(stdout);
      printf("AHA we are gonna be done soon \n");
      funlockfile(stdout);
         
   }

  while(++counter < 400)
    {
      gettimeofday(&ts, NULL);
      flockfile(stdout);
      printf("Logger flushing_1: %d %ld.%06ld\n", counter , ts.tv_sec,
ts.tv_usec);
      funlockfile(stdout);
   
      if (!last)
     usleep(100000);
     
      flockfile(stdout);
      printf("Logger flushing_2: %d %ld.%06ld\n", counter , ts.tv_sec,
ts.tv_usec);
      funlockfile(stdout);
     
      sprintf(num, " %d\n", counter);

      flockfile(stdout);
      printf("Logger flushing_3: %d %ld.%06ld\n", counter , ts.tv_sec,
ts.tv_usec);
      funlockfile(stdout);

      struct iovec vect_to_send[2];
      vect_to_send[0].iov_base=(void *)(cmd);
      vect_to_send[0].iov_len=strlen(cmd)+1;
      vect_to_send[1].iov_base=(void *)(num);
      vect_to_send[1].iov_len=strlen(num)+1;
     
     
      flockfile(stdout);
      printf("Logger flushing_4: %d %ld.%06ld\n", counter , ts.tv_sec,
ts.tv_usec);
      funlockfile(stdout);
     
/*       write(sockfd,
vect_to_send[0].iov_base,vect_to_send[0].iov_len); */
     
/*       flockfile(stdout); */
/*       printf("Logger flushing_5: %d %ld.%06ld\n", counter ,
ts.tv_sec, ts.tv_usec); */
/*       funlockfile(stdout); */
     
/*       write(sockfd,
vect_to_send[1].iov_base,vect_to_send[1].iov_len); */
     
/*       flockfile(stdout); */
/*       printf("Logger flushing_6: %d %ld.%06ld\n", counter ,
ts.tv_sec, ts.tv_usec); */
/*       funlockfile(stdout); */
         
      if (pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state)!=0)
     {
       fprintf(stderr, "error setting cancel state to disabled\n");
       abort();
     }
      else
     {
       flockfile(stdout);
       
       if (state == PTHREAD_CANCEL_ENABLE)
         {
           printf("before:DISABED TO ENABLED ERROR!!\n");
         }
       else
         {
           printf("before:DISABLED to DISABLED\n");
         }
       
       funlockfile(stdout);
     }
       


      if (writev(sockfd, vect_to_send, 2)==-1)
     {
       fprintf(stderr, "Error during writev\n");
     }
     
     
      if (pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state)!=0)
     {
       fprintf(stderr, "error setting cancel state to disabled\n");
       abort();
     }
      else
     {
       flockfile(stdout);
       
       if (state == PTHREAD_CANCEL_ENABLE)
         {
           printf("after:DISABED TO ENABLED ERROR!!\n");
         }
       else
         {
           printf("after:DISABLED to DISABLED\n");
         }
       
       funlockfile(stdout);
     }
       
     
      flockfile(stdout);
      printf("Logger flushing_5: %d %ld.%06ld\n", counter , ts.tv_sec,
ts.tv_usec);
      funlockfile(stdout);
     
      if (!last)
     break;
    }
 
  if (last)
    {
      flockfile(stdout);
      printf("LAst=true ended\n");
      funlockfile(stdout);

    }
 
  if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &state)!=0)
    {
      fprintf(stderr, "error setting cancel state to enabled\n");
      abort();
    }
  else
    {
      flockfile(stdout);
      if (state == PTHREAD_CANCEL_ENABLE)
     {
       printf("end:ENABLED to ENABLED ERROR!\n");
     }
      else
     {
       printf("end:DISABLED to ENABLED\n");
     }
     
      funlockfile(stdout);

    }
  printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
  pthread_testcancel();
}
 
void cleanup_handler_1 (void *arg)
{
  printf("cleanup_1\n");
  dump(true);
}

void *th1 (void *arg)
{
  printf("SETTING UP OF LOGGER STARTED\n");
  struct sockaddr_in     servaddr;

  if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
      fprintf(stderr,"socket error");
      exit(-11);
    }
 
  bzero(&servaddr, sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_port = htons(port);
 
  if (inet_pton(AF_INET, ip_address, &servaddr.sin_addr) <= 0)
    {
      fprintf(stderr,"inet_pton error for %s",ip_address);
      exit(-12);
    }

  if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr))
< 0)
    {
      fprintf(stderr,"connection error");
      exit(-13);
    }

  printf("SETTING UP OF LOGGER COMPLETED\n");

  pthread_cleanup_push (cleanup_handler_1, NULL);

  while(true)
    {
      dump(false);
    }

  pthread_cleanup_pop (0);

}


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

  void *result;
  int status;

  if (argc != 3)
  {
    printf("USE as %s server_ip_address port_number\n", argv[0]);
    return 0;
  }

  memcpy(ip_address, argv[1], strlen(argv[1]));
  ip_address[strlen(argv[1])]='\0';
  port = atoi(argv[2]);
 
  printf("%s %d \n", ip_address, port);
 
  status = pthread_create (&thread1, NULL, th1, NULL);
  sleep(5);

  printf("Stopping TH1\n");
  status = pthread_cancel (thread1);
  if (status != 0)
    printf("Cancel thread error");
  status = pthread_join (thread1, &result);
  if (status != 0)
    printf("Join thread error");
 
  if (result == PTHREAD_CANCELED)
    printf ("thread 1 cancelled\n");
  else
    printf ("thread 1 was not cancelled\n");
 
  return 0;
}




//server code
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/time.h>
#include <stdlib.h>
#include <memory.h>
#include <string>
#include <strings.h>
#include <iostream>
#include <map>
//#include "errors.h"

#define MAXLINE 100
#define LISTENQ 10
#define SERV_PORT 1400
#define MAX_LOG_FILES 10

using namespace std;

int listenfd, connfd;



void signal_handler(int sig)
{
  int exit_code;
 
  if (sig==SIGINT)
    {
      cout << "So you don't like me any more? Why do you interrupting
me??::" << endl;
      exit_code=0;
    }
  return;
}

void bye()
{

  cout << "reached here" << endl;
  if(close(connfd)==-1)
    {
      fprintf(stderr,"CONNFD SHUTDOWN\n");
      abort();
    }

 


}

static ssize_t my_read(int fd, char *ptr)
{
  static int     read_cnt = 0;
  static char     *read_ptr;
  static char     read_buf[MAXLINE];

  if (read_cnt <= 0)
    {
    again:
      if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
     if (errno == EINTR)
       goto again;
     return(-1);
      } else if (read_cnt == 0)
     return(0);
      read_ptr = read_buf;
    }
 
  read_cnt--;
  *ptr = *read_ptr++;
  return(1);
}

ssize_t readline(int fd, void *vptr, size_t maxlen)
{
  int     n, rc;
  char     c, *ptr;

  ptr = (char *) vptr;
  for (n = 1; n < maxlen; n++)
    {
      if ( (rc = my_read(fd, &c)) == 1)
     {
       *ptr++ = c;
       if (c == '\n')
         break;     /* newline is stored, like fgets() */
     } else if (rc == 0) {
       if (n == 1)
         return(0);     /* EOF, no data read */
       else
         break;          /* EOF, some data was read */
     } else
       return(-1);          /* error, errno set by read() */
    }
 
  *ptr = 0;     /* null terminate like fgets() */
  return(n);
}
/* end readline */

ssize_t Readline(int fd, void *ptr, size_t maxlen)
{
  ssize_t          n;
 
  if ( (n = readline(fd, ptr, maxlen)) < 0)
    {
      fprintf(stderr,"readline error");
      exit(-14);
    }
  return(n);
}

ssize_t      writen(int fd, const void *vptr, size_t n)
{
  size_t          nleft;
  ssize_t          nwritten;
  const char     *ptr;
 
  ptr = (const char*)vptr;
  nleft = n;
  while (nleft > 0) {
    if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
      if (errno == EINTR)
     nwritten = 0;          /* and call write() again */
      else
     return(-1);               /* error */
    }

    nleft -= nwritten;
    ptr   += nwritten;
  }
  //cout << "chars written " << n << endl;
  return(n);
 
}
/* end writen */

void Writen(int fd, void *ptr, size_t nbytes)
{
  if (writen(fd, ptr, nbytes) != nbytes)
    {
      fprintf(stderr,"writen error");
      exit(-14);
    }
  //  cout << "written" << endl;
}

/* Read "n" bytes from a descriptor. */
ssize_t     readn(int fd, void *vptr, size_t n)
{
  size_t     nleft;
  ssize_t     nread;
  char     *ptr;

  ptr = (char*)vptr;
  nleft = n;
  while (nleft > 0) {
    if ( (nread = read(fd, ptr, nleft)) < 0) {
      if (errno == EINTR)
     nread = 0;          /* and call read() again */
      else
     return(-1);
    } else if (nread == 0)
      break;                    /* EOF */

    nleft -= nread;
    ptr   += nread;
  }
  return(n - nleft);          /* return >= 0 */
}
/* end readn */

ssize_t Readn(int fd, void *ptr, size_t nbytes)
{
  ssize_t          n;
  if ( (n = readn(fd, ptr, nbytes)) < 0)
    {  
      fprintf(stderr,"readn error");
      exit(-12);
    }

  return(n);
}


void log(int sockfd)
{
  char line[100];
  ssize_t n;

  for (; ;)
    {
      if ( (n=Readline(sockfd, line, 100)) == 0)
     return;
      printf("%s\n", line);
    }
}


int main(int argc, char **argv)
{
  atexit(bye);

  struct sigaction sa;
  sa.sa_handler = signal_handler;
  sigemptyset(&sa.sa_mask);
  sigaction(SIGINT, &sa, NULL);



  pid_t childpid;
  socklen_t clilen;

  struct sockaddr_in     servaddr, cliaddr;

  if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
      fprintf(stderr,"socket error");
      exit(-11);
    }

  if (argc != 2)
    fprintf (stderr,"use like %s <port number> <n", argv[0]);

  int port = atoi(argv[1]);
 
  int reuse =1;
  if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse,
sizeof(reuse))==-1)
    {
      fprintf(stderr,"setsockopt error\n");
      abort();
    }


     

  bzero(&servaddr, sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_port = htons(port);
  servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

  if ((bind(listenfd, (struct sockaddr *) &servaddr,
sizeof(servaddr))) < 0)
    {
      fprintf(stderr,"socket bind error");
      exit(-12);
    }

  if ((listen(listenfd, LISTENQ)) < 0)
    {
      fprintf(stderr,"socket listen error");
      exit(-13);
    }

  for ( ; ; )
    {
      clilen = sizeof(cliaddr);
      if ((connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
&clilen)) < 0)
     {
       fprintf(stderr,"socket accept error");
       exit(-14);
     }
     
      if ( (childpid = fork()) == 0)
     {
       close(listenfd);
       log(connfd);
       exit(0);
     }
      close(connfd);
    }
}

Comment 1 Jakub Jelinek 2004-04-07 11:24:19 UTC
Please try RHEL3 or FC1 or FC2t2 glibc instead, there has been several
fixes related to PTHREAD_CANCEL_DISABLE.


Note You need to log in before you can comment on or make changes to this bug.