Subject: lib/20001: native pthreads, signals and cond. variables
To: None <gnats-bugs@gnats.netbsd.org>
From: Anthony Mallet <anthony.mallet@useless-ficus.net>
List: netbsd-bugs
Date: 01/22/2003 22:55:13
>Number:         20001
>Category:       lib
>Synopsis:       native pthreads, signals and cond. variables
>Confidential:   no
>Severity:       critical
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Jan 22 13:56:00 PST 2003
>Closed-Date:
>Last-Modified:
>Originator:     Anthony Mallet
>Release:        NetBSD 1.6M - 2003-01-21
>Organization:
>Environment:
System: NetBSD ficus 1.6M NetBSD 1.6M (FICUS) #9: Wed Jan 22 01:35:03 CET 2003 troot@ficus:/home/src/netbsd-current/sys/arch/i386/compile/FICUS i386
Architecture: i386
Machine: i386

         -lpthread.0 => /usr/lib/libpthread.so.0
         -lc.12 => /usr/lib/libc.so.12

	 Jan 22 00:47 /usr/lib/libpthread.so.0.1

>Description:
I'm trying to use the new native libpthread with condition variables
signaled from signal handlers. The whole software is a bit complicated,
but I've written a simple C test program (which I attach below) that
suffers from the same bugs.

Basically, the program does the following:
1. Create a timer, with setitimer and a SIGALRM handler
2. Wait on a condition variable
3. The SIGALRM handler then signals the cond. variable
(this might look useless here, but in the context of the whole software
it does make sense ;)

When used with only one thread, this triggers an assert in libpthread:
assertion "next != 0" failed: file
"/home/src/netbsd-current/lib/libpthread/pthread_run.c", line 117,
function "pthread__next"

When used with a 2nd (dummy) thread that returns _before_ the SIGALRM is
handled, this prevent the program from returning from pthread_cond_wait

When used with a 2nd thread that returns _after_ the SIGALRM is handled,
this works. (Yeah !)

See the code below for a clearer description :)

>How-To-Repeat:

Compile and run the program below, it should produce the following
output:

ficus[~] > cc -Wall -o pthread-bug pthread-bug.c -lpthread

ficus[~] > ./pthread-bug one
Using 1 threadbefore cond wait
before cond wait
assertion "next != 0" failed: file
"/home/src/netbsd-current/lib/libpthread/pthr
ead_run.c", line 117, function "pthread__next"
Abort (core dumped)

ficus[~] > ./pthread-bug two
Using 2 threads, counting to 2
before cond wait
thread 2 started
thread 2 stopped
before cond signal
after cond signal
^C
(must interrupt by hand)

ficus[~] > ./pthread-bug 1000000000
Using 2 threads, counting to 1000000000
before cond wait
thread 2 started
before cond signal
after cond signal
after cond wait
ficus[~] >
(it works)

----------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/time.h>

pthread_cond_t	cond;

void	*	thread_two(void *);
void		timer_func(int);

int
main(int argc, char *argv[])
{
   pthread_t		thread;
   pthread_mutex_t	lock;
   struct itimerval	tv;
   int			option;
   int			status;

   if (argc < 2) {
      printf("Usage: %s [one|two|<integer>]\n", argv[0]);
      return 2;
   }
   if (!strcmp(argv[1], "one")) {
      option = 1;
      printf("Using 1 thread", option);
   } else if (!strcmp(argv[1], "two")) {
      option = 2;
      printf("Using 2 threads, counting to %d\n", option);
   } else {
      option = strtol(argv[1], NULL, 10);
      printf("Using 2 threads, counting to %d\n", option);
   }

   /* intialize a mutex */
   status = pthread_mutex_init(&lock, NULL);
   if (status) { perror("pthread_mutex_init"); return 2; }

   /* initialize a condition variable */
   status = pthread_cond_init(&cond, NULL);
   if (status) { perror("pthread_cond_init"); return 2; }

   /* create a second thread, according to the command-line option */
   if (option > 1) {
      status = pthread_create(&thread, NULL, thread_two, &option);
      if (status) { perror("pthread_create"); return 2; }
   }

   /* prepare for receiving SIGALRM */
   signal(SIGALRM, timer_func);

   /* setup a timer that starts after 1s */
   tv.it_interval.tv_usec = 0;
   tv.it_interval.tv_sec = 0;
   tv.it_value.tv_usec = 0;
   tv.it_value.tv_sec = 1;

   status = setitimer(ITIMER_REAL, &tv, NULL);
   if (status) { perror("setitimer"); return 2; }

   /* block on the condition variable */
   pthread_mutex_lock(&lock);
   printf("before cond wait\n");
   pthread_cond_wait(&cond, &lock);
   printf("after cond wait\n");
   pthread_mutex_unlock(&lock);

   return 0;
}

/* Second thread body */
void *
thread_two(void *length)
{
   int i = *(int *)length;

   printf("thread 2 started\n");
   while(i--) /* empty body */;
   printf("thread 2 stopped\n");

   return NULL;
}

/* SIGALRM handler */
void
timer_func(int dummy)
{
   printf("before cond signal\n");
   pthread_cond_signal(&cond);
   printf("after cond signal\n");
}

>Fix:
No idea :(

>Release-Note:
>Audit-Trail:
>Unformatted: