Date: Tue, 28 Dec 1999 18:01:53 -0600 From: "Richard Seaman, Jr." <dick@tar.com> To: Jason Evans <jasone@canonware.com> Cc: hackers@freebsd.org Subject: Cancellation points in Linuxthreads Message-ID: <19991228180153.B290@tar.com>
next in thread | raw e-mail | index | archive | help
[-- Attachment #1 --]
The new version of linuxthreads now seems extend cancellation points
into libc. However, it does so in a manner that threads can get
cancelled within libc with libc holding internal locks, resulting
in deadlock.
I think the following example program demonstrates this. It works
under libc_r, because libc_r carefully unwinds any internal locks
created within libc. Linuxthreads does not have this capability,
and the sample program deadlocks (at least it does here).
--
Richard Seaman, Jr. email: dick@tar.com
5182 N. Maple Lane phone: 262-367-5450
Chenequa WI 53058 fax: 262-367-5852
[-- Attachment #2 --]
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define BUFFSIZE 100000
int StateSet = 0;
/* Use these to keep track of where we are. Can't use
* printf's in the thread, since printf has become a
* cancellation point.
*/
int ThreadReadStart = 0;
int ThreadReadError = 0;
int ThreadReadComplete = 0;
int ThreadRewindComplete = 0;
int ThreadRewindStart = 0;
/* Install latest linuxthreads port and compile as:
* compile as gcc -Wall -I/usr/local/include/pthread/linuxthreads -L/usr/local/lib -llthread -llgcc_r-o testcancel testcancel.c
*/
FILE *testfile;
void * test_thread (void * arg)
{
char in;
int oldstate;
int oldtype;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
StateSet = 1;
/* Simple synchronization to allow main to cancel us */
sched_yield();
/*
* main should have cancelled us by now. But since we're of type
* DEFERRED, we only cancel at a cancellation point.
*/
ThreadRewindStart = 1;
/*
* It appears that rewind() has become a cancellation point.
*/
rewind (testfile);
ThreadRewindComplete = 1;
ThreadReadStart = 1;
if (fread (&in, sizeof (in), 1, testfile) < 1)
ThreadReadError = 1;
else
ThreadReadComplete = 1;
return (NULL);
}
int main()
{
pthread_t th;
char in;
char *buffer;
buffer = malloc (BUFFSIZE);
memset (buffer, 'A', BUFFSIZE);
testfile = fopen ("/tmp/testcancel", "w+");
if (testfile == NULL ||
fwrite (buffer, sizeof (*buffer), BUFFSIZE, testfile) < BUFFSIZE)
exit(1);
pthread_create(&th, NULL, test_thread, NULL);
printf ("Thread created\n");
/* Simple syncronization to make sure the thread runs and sets its cancellation state */
if (StateSet == 0)
sched_yield();
/* The thread's cancellation state and type should now be set */
printf ("Cancelling thread\n");
pthread_cancel (th);
printf ("Thread cancelled\n");
/* Sleep to let the thread resume so it can do fread() */
sleep (2);
printf ("Main thread sleep done\n");
printf ("ThreadRewindStart = %i\n", ThreadRewindStart);
printf ("ThreadRewindComplete = %i\n", ThreadRewindComplete);
printf ("ThreadReadStart = %i\n", ThreadReadStart);
printf ("ThreadReadError = %i\n", ThreadReadError);
printf ("ThreadReadComplete = %i\n", ThreadReadComplete);
printf ("Starting rewind in main\n");
/* funlockfile (testfile); Doesn't help */
rewind (testfile);
/*
* We should get here. If we don't, its because the thread has
* cancelled holding a lock in libc, so we're deadlocked. It's
* ok for us to cancel a thread that holds a lock we created
* (ok in the sense we're allowed to deadlock ourselves if we're
* that stupid), but libc shouldn't silently do it to us.
*
* We can save ourselves by unlocking testfile, but we shouldn't have
* to clean up after libc. However, even this doesn't work, since
* funlockfile requires that we be the owner of the lock before we
* can unlock it.
*/
printf ("Starting read in main\n");
if (fread (&in, sizeof (in), 1, testfile) < 1)
printf ("fread error in main\n");
else
printf ("fread completed in main\n");
fclose (testfile);
return 0;
}
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?19991228180153.B290>
