pthread_cancel on Windows
September 8, 2010Edward, I’m afraid I have some bad news. Your interruptible GHC patch; it was involved in a terrible accident on the way to Windows portability. I hope you understand: we’re doing our best to patch it up, but there have been some complications…
Pop quiz! What does this pthreads code do? :
#include <pthread.h>
#include <stdio.h>
void *thread1(void *arg) { sleep(10000); }
void *thread2(void *arg) { while (1) {} }
void *psycho_killer(void *arg) {
pthread_t *id = (pthread_t*)arg;
pthread_cancel(*id);
printf("[%p] Psycho killer...\n", id);
pthread_join(*id, NULL);
printf("[%p] ...qu'est-ce que c'est.\n", id);
}
int main(char* argv, int argc) {
pthread_t t1, t2, k1, k2;
pthread_create(&t1, NULL, thread1, NULL);
printf("[%p] I can't sleep 'cause my bed's on fire\n", &t1);
pthread_create(&t2, NULL, thread2, NULL);
printf("[%p] Don't touch me I'm a real live wire\n", &t2);
pthread_create(&k1, NULL, psycho_killer, &t1);
pthread_create(&k2, NULL, psycho_killer, &t2);
pthread_join(k1, NULL);
pthread_join(k2, NULL);
printf("Run run run away!\n");
return 0;
}
It never manages to terminate the second thread…
ezyang@javelin:~/Desktop$ ./test
[0xbf900b4c] I can't sleep 'cause my bed's on fire
[0xbf900b48] Don't touch me I'm a real live wire
[0xbf900b4c] Psycho killer...
[0xbf900b4c] ...qu'est-ce que c'est.
[0xbf900b48] Psycho killer...
^C
If you just had the pthread_cancel and the pthread_setcancelstate manpages, this might seem a little mysterious. The pthreads page, however, makes things clear: sleep is among one-hundred and two “cancellable” functions, which pthread_cancel must terminate within if a thread’s cancellability status is PTHREAD_CANCEL_DEFERRED (there are another two-hundred and forty-two which may or may not be cancelled). If the thread is stuck in userspace, it has to explicitly allow a deferred cancellation with pthread_testcancel. Previous versions of the POSIX spec were a little unclear whether or not cancellation should take place upon entry to the system call, or while the system call was running, but the 2008 spec is fairly clear:
Cancellation points shall occur when a thread is executing the following functions…
The million-dollar question is: “Can we implement the same semantics on Windows?” Actually, since it seems that a lot of people would have wanted pthreads functionality on Windows, you would think that this has been already been implemented by pthreads-win32. We turn to the source! :
if (tp->cancelType == PTHREAD_CANCEL_ASYNCHRONOUS
&& tp->cancelState == PTHREAD_CANCEL_ENABLE
&& tp->state < PThreadStateCanceling)
{
/* snip */
}
else
{
/*
* Set for deferred cancellation.
*/
if (tp->state < PThreadStateCancelPending)
{
tp->state = PThreadStateCancelPending;
if (!SetEvent (tp->cancelEvent))
{
result = ESRCH;
}
}
else if (tp->state >= PThreadStateCanceling)
{
result = ESRCH;
}
(void) pthread_mutex_unlock (&tp->cancelLock);
}
Interestingly enough, pthreads-win32 doesn’t seem to do anything special: when we translate our test program and run it with pthreads-win32, it gets stuck on the Sleep call as well:
C:\Users\ezyang\pthreads-win32\Pre-built.2\lib>test.exe
[0022FF40] I can't sleep 'cause my bed's on fire
[0022FF38] Don't touch me I'm a real live wire
[0022FF40] Psycho killer...
[0022FF38] Psycho killer...
^C
At this point, it’s worth stepping back for a moment and asking, “What are we really trying to do here?” If you were to ask how to terminate threads on, say, Stack Overflow, you’d get a bunch of responses telling you, “Stop that and do it the right way”; namely, by explicitly handling thread termination on the thread itself via another message passing mechanism.
So there are number of different needs for interruptible calls:
- GHC would like to be able to put blocking IO calls on a worker thread but cancel them later; it can currently do this on Linux but not on Windows,
- Users would like to write interrupt friendly C libraries and have them integrate seamlessly with Haskell’s exception mechanism, and
- We’d like to have the golden touch of the IO world, instantly turning blocking IO code into nice, well-behaved non-blocking code.
Next time I’ll talk about what different approaches might be needed for each of these goals.
4 Comments