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: [EMAIL PROTECTED] 5182 N. Maple Lane phone: 262-367-5450 Chenequa WI 53058 fax: 262-367-5852
#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; }