Christoph Nahr said:
??? I completely disagree, and I don't really see any reasons to make
such broad-sweeping statements.
Why? because you don't like it or you because you think I'm wrong. If it's
the latter we will have to agree to disagree.
Maybe it's too strongly worded, but following is what's been suggested by
devs. on the CLR team (C Brumme, J. Duffy) and outside Microsoft; "Most code
should never try to abort an opaque thread..." "It's recommended that
Framework code is written with the assumption that an abort will shortly be
followed by a AD unload".
So now it's safe even for static constructors? That's fantastic, I
wasn't aware of that! That solves the last real issue I know of.
Really and what about this:
using(Resource r = new(...)
{
...
}
where the resource acquisition occurs outside the try block (if you look at
what's been emitted by the C# compiler), that means that finally won't run
(Dispose won't be called) in the presence of an Asynchronous exception like
ThreadAbort that occurs after acquiring but before assignment of the
reference to r, there goes your deterministic clean-up, so you are at the
mercy of the finalizer to release the resource acquired.
Sure, but that's simply because Thread.Abort raises an *asynchronous*
exception, and you have to be aware exactly what that means. I really
can't see why that should be a reason to avoid it, though.
Maybe <be aware exactly what that means> is the reason why you can't see a
reason to avoid it.
Lets look at another sample....
Suppose you are interrupted by a thread abort while modifying a complex data
structure (a container), leaving the container in a partly modified state.
Your container is corrupted, right?
No problem you say, the GC will clean-up the garbage when there is only one
single reference, but what if this container was shared between threads? How
does the thread knows the container is in a corrupted state? It doesn't,
unless the thread calling Thread.Abort is also the thread that has a
reference to the shared object and simply recovers the object state somehow.
But what when more threads share the object......
Yeah, but forgive me, isn't that just saying "bad programming is bad"?
Why?
Do you assume this is better programming?
IntPtr handle = IntPtr.Zero;
try {
handle = AcquireHandle(....); // Abort after acquiring but before
assigning to handle
...
}
finally {
if(handle != IntPtr.Zero) FreeUnmanaged(handle); // handle is
null, so handle leak
}
or this....
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(100000);
}
finally
{
if(ptr!= IntPtr.Zero)
Marshal.FreeHGlobal(ptr);
}
No it isn't, if the abort occurs after memory allocation but before ptr
assignment, you'll end with ptr still being "null" when your finally runs,
bummer memory leak. This isn't an issue when you unload the AD, but what if
you continue to execute code inside this domain?
Where a ThreadAbortException is possible you just must not do that.
That's the point of asynchronous exceptions, they might occur at any time.
It's another rule to remember and follow, but not an impossible one.
No, for me rule to remember is "Never initiate asynchronous thread aborts in
Framework code, unless it's part of an AD unload", there are more secure
alternatives to stop a thread from making progress.
Willy.