From astra@mtx.coollist.com  Wed Jun 14 22:08:02 2000
Return-Path: <astra@mtx.coollist.com>
Received: from mtx.coollist.com (mtx.coollist.com [207.8.172.27])
	by hub.freebsd.org (Postfix) with SMTP id 0F66837B582
	for <FreeBSD-gnats-submit@freebsd.org>; Wed, 14 Jun 2000 22:08:01 -0700 (PDT)
	(envelope-from astra@mtx.coollist.com)
Received: (qmail 88754 invoked by uid 0); 15 Jun 2000 05:07:06 -0000
Message-Id: <20000615050706.88753.qmail@mtx.coollist.com>
Date: 15 Jun 2000 05:07:06 -0000
From: astra@astraweb.com
Sender: astra@mtx.coollist.com
To: FreeBSD-gnats-submit@freebsd.org
Subject: a problem in pthread_mutex_trylock
X-Send-Pr-Version: 3.2

>Number:         19292
>Category:       kern
>Synopsis:       a problem in pthread_mutex_trylock
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    jasone
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Jun 14 22:10:03 PDT 2000
>Closed-Date:    Tue Jul 11 15:38:55 PDT 2000
>Last-Modified:  Tue Jul 11 15:41:09 PDT 2000
>Originator:     astra@astraweb.com
>Release:        FreeBSD 4.0-RELEASE i386
>Organization:
Astra Labs
>Environment:

	

>Description:
/*
  Demonstration program to show problems with pthread_try_lock() and 
  FreeBSD 3.1 in threads.


  ** error description **
  
  Under FreeBSD, once all the threads have been used up (all the mutexes have
  been locked), the pthread_try_lock() call always returns that the mutex is 
  locked, even when it isn't.  The code follows through to the
  pthread_mutex_lock() which succeeds on a mutex that had returned lock under 
  pthread_try_lock().
  
  This leads to the following log messages:
  
 ** all threads used up... will sit on thread 59...
 ++ giving data to thread 59
 ** all threads used up... will sit on thread 190...
 ++ giving data to thread 190
 ** all threads used up... will sit on thread 175...
 ++ giving data to thread 175
 ** all threads used up... will sit on thread 246...
 ++ giving data to thread 246
 ** all threads used up... will sit on thread 117...
 ++ giving data to thread 117
 ** all threads used up... will sit on thread 177...
 ++ giving data to thread 177
 ** all threads used up... will sit on thread 234...
  
  The resulting trouble with this error is that the code executes a lot 
  slower, as it can't find free threads and must wait for a random given
  thread to come available using pthread_mutex_lock().


  
  ** author information **
  
  Written by Roger Nesbitt (roger@ecosm.com) on 7 June 2000.
  Adapted from another program, hence the extraneous includes and variables.


  ** compile information **

  Compiled under Linux with: gcc -ottest -lpthread ttest.c
  Compiled under FreeBSD with: gcc -ottest -pthread ttest.c
*/
	

>How-To-Repeat:

// The following code is available at http://www.ecosm.com/ttest.c also.

#include <ctype.h>
#include <stdarg.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>

#define num_threads 250

pthread_t thread[num_threads];
pthread_mutex_t thread_mutex[num_threads];
char thread_buf[num_threads][1024];


void log(int lvl, char *fmt, ...)
{
        char buf[4096];
        
        va_list ap;
        va_start(ap, fmt);
        vsnprintf(buf, 4096, fmt, ap);
        va_end(ap);
        
        fprintf(stderr, buf);
}



// The check() function is the (only) function that is threaded.

void *check(void *threadv)
{
        char    *buf;
        int             mythread = ((int)threadv);
        struct  timeval tv;
                
        buf = thread_buf[mythread];     
        log(15, " -- thread %d started\n", mythread);

        log(15, "%d: in this thread is '%s'\n", mythread, buf);

        // pause for a random amount of time.
        
        tv.tv_sec = (random() % 6) + 2;
        tv.tv_usec = random() % 1000000;
        select(0, NULL, NULL, NULL, &tv);

        log(15, " -- thread %d ended\n", mythread);
        pthread_mutex_unlock(&thread_mutex[mythread]);          
        return NULL;
}


int init_threads(void)
{
        int i;
        
        for (i=0; i<num_threads; i++)
                pthread_mutex_init(&thread_mutex[i], NULL);

        bzero(thread, sizeof(thread));

        return 0;
}
// This routine finds a free thread by searching an array of mutex
// locks.  If the mutex is unlocked, the thread is free to use.

int find_free_thread(void)
{
        int i, n;

        for (i=0; i<num_threads; i++)
        {
                // Under FreeBSD 3.1, the following code will always evaulate 
to ELOCK
                // once the "all threads used up" code below has been called.
                        
                if ((n = pthread_mutex_trylock(&thread_mutex[i])) == 0)
                {
                        pthread_join(thread[i], NULL);
                        return i;
                }
                else
                {       
                        if (n != EBUSY)
                                log(8, "Strange, pthread_mutex_trylock 
returned %d\n", n);
                }
                        
        }

        // If there are no free threads, choose one randomly and wait until 
it's free.

        i = random() % num_threads;

        log(15, " ** all threads used up... will sit on thread %d...\n", i);

        pthread_mutex_lock(&thread_mutex[i]);
        pthread_join(thread[i], NULL);
        return i;
}


// Finds a free thread and starts it.

int thread_it(char *buf)
{
        int     n;
        
        n = find_free_thread();
        log(15, " ++ giving data to thread %d\n", n);
        strcpy(thread_buf[n], buf);
        pthread_create(&thread[n], NULL, check, (void *)n);
}       


// Waits for all existing threads to finish.

void wait_for_threads(void)
{
        int     n;
        
        log(4, "\n !! Waiting for threads to finish...\n");        
        for (n=0; n<num_threads; n++)
                pthread_join(thread[n], NULL);
}       






int main(int argc, char *argv[])
{
        int i;
        char s[1024];
        
        init_threads();
        
        for (i=0; i<1000; i++)
        {
                sprintf(s, "%d", i);
                thread_it(s);
        }

        wait_for_threads();

        return 0;
}



	

>Fix:

	


>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: gnats-admin->freebsd-bugs 
Responsible-Changed-By: phantom 
Responsible-Changed-When: Sat Jul 8 06:44:39 PDT 2000 
Responsible-Changed-Why:  
Misfilled PR 

http://www.freebsd.org/cgi/query-pr.cgi?pr=19292 
Responsible-Changed-From-To: freebsd-bugs->jasone 
Responsible-Changed-By: jasone 
Responsible-Changed-When: Mon Jul 10 09:38:01 PDT 2000 
Responsible-Changed-Why:  
Over to maintainer. 
State-Changed-From-To: open->closed 
State-Changed-By: jasone 
State-Changed-When: Tue Jul 11 15:38:55 PDT 2000 
State-Changed-Why:  
A mutex must be unlocked by the same thread that locked it.  If you want to 
use the programming idiom demonstrated in the program, use semaphores 
instead of mutexes. 
>Unformatted:
