On 30 Jan 2002 12:41:22 -0500, Sam Steingold <sds@...> wrote:
>> * In message <3c57defc.239941875@...>
>> * On the subject of "[clisp-list] Solution to TerminateThread bug."
>> * Sent on Wed, 30 Jan 2002 15:20:04 GMT
>> * Honorable olczyk@... (Thaddeus L. Olczyk) writes:
>> Attached is a diff file which shows what needs to be done to fix the
>> TerminateThread problem.
>next time, please use context (or unified) diffs - more reliable.
Sorry. I'm new to patch, and my bibble on UNIX--Kernigan and Pike
don't mention it ( at least not in their index).
>> Applying the patch might be a problem ( at least I don't have patch
>> on my Windows box ).
>cygwin comes with diff, patch &c - no problems.
I must have missed patch.
>> Here is a description of the code around the bug, what it does, what
>> the problem is and how I fixed it. ( If anyone really cares.)
>It boils down to the fact that TerminateThread() tries to kill a dead
>thread and there is no way to check whether the thread is dead or alive.
Not quite. The problem is that you test for whether the thread is
alive or not ( by the interruptible_active flag ), to soon. By the
time you get around to calling the thread, it may have already died.
The problem is also the interweaving of contexts which means that
the thread has a chance to die before TerminateThread is called.
Understand this is a funcamental problem in your approach. Even
if you could find out whether the thread is alive or dead (via an API
call), you have the problem of the thread dieing before you could call
The way around this is to use some kind of synchronization, ie a
critical section or a mutex. However since TerminateThread does not
clean up afterwards you risk a deadlock.
A typical situation might be that the conditional surrounding
TerminateThread acquires a critical section. The child thread
is near exit and tries to acquire the same critical section to
set the interruptible_active flag. Since it can't it blocks.
The TerminateThread conditional then actually calls TerminateThread.
It then releases the the critical section. The child thread is now
dead by the attempt to acquire the critical section will now go
forward. The child thread now has the critical section, but it can't
kill it because the thread is dead. Next time around whatever tries
to acquire the critcal section will block. Deadlock.
The problem (as Todd pointed out ) is that TerminateThread
doesn't clean up after itself.
>Your solution basically tries to wait for the thread to die - instead
>of just killing it outright, and you said it does not always work.
It should work all the time ( a little but here their might be a need
for tweaking, let me first explain this ). The solution does not wait
for the thread to die. Here is what the solution does:
} //2 thread dies
On a single processor machine the system is constantly switching
between threads. Call that context switch CS. One order of execution
In this case the parent tests to see if the thread is alive. It gets a
yes. Then a context switch sets the same variable to false. The child
thread is dieing, but the parent thread thinks it's not. The child
then dies and the parent then tries to terminate it.
An alternate order of execution could be=20
In this case the variable is set to zero, when 3 tests
interruptible_thread it discovers that it is false, the child is
dieing on it's own, and does not call TerminateThread.
Another order of execution could be
in which case
the parent thread tests interruptible_active and thinks the child
is not dieing. The child announces that it is going to die, but before
it actually dies the parent calls terminate thread and kills it.
This is a bit simplistic as the compiler/operating system are doing
things in between the statement and for that reason a statement
may encounter several context switches before dieing.=20
what my solution does is force context switches to make sure that
4 is executed before 2. ( 4 before 2 is good, 2 before 4 is bad. )
Sleep(0); // 1.1=09
Sleep(0); // 1.2
Sleep(0); // 1.3=09
} //2 thread dies
Sleep(0) tells the system to go to sleep for 0 milliseconds.
It also tells it ( as Sleep always does ) to do a context switch.
Essentially it is the same as yield.
So look at the first sequence
So 4 gets executed before 2 because 1.1forces a context switch.
Now back to the tweaking. As I said sometimes multiple context
switches can happen per statement.
so the modified first sequence might look like.
3,CS,1,1.1,CS, first part of 4,CS,1.2,CS,second part of four.
The trick is to force enough context switches to make sure that
four is complete ( or at least that the actual kill thread process
has been put in place ). I'm fairly sure that 2 is enough, but I threw
in the third just to make sure.
I just realised that I wasn't addressing the original code, but a
quick look will suggest that it's even worse there.
} //1 thread dies
now the deadly sequence is 1 before 4.
The order 1,2,4 always has to happen ( 2 is waiting for 1).
So you can have something as bad as
way too late for 5.
or something well behaved
1,2,5,CS,3 4 never gets executed.
3,first part of 4,CS,first part of 1,CS second part of 4, which is OK
because 4 finishes before 1.
Or something like
1,first part of 2,CS,3,4,CS,second part of 2,5
>Is there a solution that would always work?
This should always work, just put in enough Sleep(0); s.
>Maybe you could follow the direction outlined by Todd and eliminate the
>need for TerminateThread() altogether?
I think you need another mechanism to signal the function to end, but
what it should be I'm not sure. This solution should work fine for