c# ReaderWriterLockSlim corrupt after interrupt thread that invokes ExitReadLock - c#

I encountered a scenario where ReaderWriterLockSlim seems to get broken after a series of legal actions.
The flow is:
Thread 1 takes writer lock1
Thread 2 tries to take reader lock1 - blocks
Thread 2 is interrupted, and calls lock1.ExitReadLock
Thread 2 didn't get the lock. It seems that an exception should have been thrown.
Thread 1 exits writer lock of lock1
Any thread that tries to take lock1.EnterReadLock will block forever
After stage 3 above, a debugger shows that lock1.CurrentReadCount is corrupt - seems to have overflowed down to 0x7FFFFFF.
I wonder if anyone had encountered this, or maybe I'm missing something.
The code that reproduces it:
[TestMethod]
public void ReproTest()
{
var rwlock = new ReaderWriterLockSlim();
rwlock.EnterWriteLock();
bool taken = false;
var reader = new Thread(() =>
{
try
{
rwlock.EnterReadLock();
s_logger.Info("Enter");
}
catch (ThreadInterruptedException)
{
rwlock.ExitReadLock();
}
});
reader.Name = "Reader";
reader.Start();
Thread.Sleep(1000);
reader.Interrupt();
Thread.Sleep(1000);
rwlock.ExitWriteLock();
while (!taken)
{
taken = rwlock.TryEnterReadLock(1000);
}
Thread.Sleep(1000);
}

This looks like a bug in the framework (tested on v3.5 and v4.0). The ExitReadLock() should throw a SynchronizationLockException, but doesn't in this case. Indeed, you can trigger a very similar issue much more simply with the following:
rwlock.EnterReadLock();
rwlock.ExitReadLock();
// This should throw a SynchronizationLockException but doesn't
rwlock.ExitReadLock();
// At this point, rwlock.CurrentReaderCount = 0x0fffffff
(In fact, ExitReadLock() will corrupt the lock if it's called without a matching EnterReadLock() on any thread that has previously entered the lock.)
The issue only occurs when the ReaderWriterLockSlim is created using the parameterless constructor, or with LockRecursionPolicy.NoRecursion. If created with LockRecursionPolicy.SupportsRecursion, it will not be corrupted by the unmatched ExitReadLock().
If you expect the reader thread to be interrupted whilst waiting for entry into lock, I would suggest changing the reader thread method to:
var reader = new Thread(() =>
{
var entered = false;
try
{
rwlock.EnterReadLock();
entered = true;
s_logger.Info("Enter");
}
finally
{
if (entered) rwlock.ExitReadLock();
}
});

The reader is never entering the read lock. It sits waiting for the write to be released. When it's interrupted, you then try to exit even though you never entered, causing read count to go below 0 I suppose :)

Code that fixes what #Lasse and #Jeremy have pointed out:
static public void ReproTest()
{
var rwlock = new ReaderWriterLockSlim();
rwlock.EnterWriteLock();
s_logger.Info("0:Enter");
bool taken1 = false;
var reader = new Thread(() =>
{
try
{
rwlock.EnterReadLock();
s_logger.Info("1:Enter");
// only set to true if taken
taken1 = true;
}
catch (ThreadInterruptedException)
{
// only release if taken
if (taken1)
rwlock.ExitReadLock();
taken1 = false;
}
});
reader.Name = "Reader";
reader.Start();
Thread.Sleep(1000);
reader.Interrupt();
Thread.Sleep(1000);
rwlock.ExitWriteLock();
// 2nd taken variable here only so we can see state of taken1
bool taken2 = taken1;
while (!taken2)
{
taken2 = rwlock.TryEnterReadLock(1000);
s_logger.Info("2:Enter");
}
Thread.Sleep(1000);
}
When run, the debug output correctly shows the write lock being taken, the 1st read lock NOT taken, and the 2nd read lock taken:
0:Enter
A first chance exception of type 'System.Threading.ThreadInterruptedException' occurred in mscorlib.dll
The thread 'Reader' (0x1358) has exited with code 0 (0x0).
2:Enter

Related

A bug that occurs during the synchronization test of a thread. Task cannot block the thread from running the following code

Questions are as follows:
According to MSDN, when a thread acquires the lock, Monitor.Enter blocks other threads until the thread releases the lock. However, during the test, Monitor.Enter (object) can block other threads from running the following code block in the case of Thread, and it still runs the following code block without blocking in the case of Task.
class program
{
static void Main(string[] ars)
{
for(int i = 0; i <5; i++)
{
new Thread(Func){ IsBackground = true}.Start();
}
Console.ReadKey();
//for(int i = 0;i< 5;i++)
//{
// Task.Factory.StartNew(Func);
//}
Console.ReadKey();
}
static void Func()
{
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}");
bool b = false;
Monitor.Enter(locker, ref b);
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} 执行 b = {b}");
}
}
The result:
Task:
Thread:
Result: As shown in the figure above, Task performs Monitor.Enter (locker) and executes the following code twice, while Thread executes it only once.
Reason: Personally suspect that the same thread can continue to run without releasing the lock?
Hope answer
Monitor locks can be entered by the same thread repeatedly several times and will unlock it once the same number of releases are done. This is called reentrancy.
private void MonitorSameThreadTest() {
var obj = new object();
lock (obj) {
// Lock obtained. Must exit once to release.
// No *other* thread can obtain a lock on obj
// until this (outermost) "lock" completes.
lock (obj) {
// Same thread can actually enter again
var lockTaken = false;
try {
Monitor.Enter(obj, ref lockTaken);
// Same thread can actually enter again
// We've *re-entered* lock again.
}
finally {
if (lockTaken) Monitor.Exit(obj);
// Must exit twice to release.
}
}
// Must exit once to release.
}
// the lock allowing other threads to obtain it.
}

does aborting a thread while in 'using' block dispose used instance

I'm starting a thread like this:
nameOfThread = new Thread(() =>
{
//do stuff
});
nameOfThread.Start();
At some point inside this anonymous function I open a WinSCP session like this:
using (Session session = new Session())
{
//do stuff
}
If I abort the thread (from somewhere else) like this nameOfThread.Abort() while still doing stuff inside using, is the session disposed at the end?
Most likely it will, but you can't be sure.
According to the documentation:
When this method [Abort] is invoked on a thread, the system throws a ThreadAbortException in the thread to abort it.
And we know exceptions will still let using statements dispose, as they should. (Give and take a few exceptions)
On the other hand, if you can end the thread gracefully, for example with a CancellationTokenSource, it would be a lot nicer for your app. It will offer much more control over the actual termination of your thread and the handling of exceptions.
I answered you can guarantee that the using statement will always call Dispose and I stand corrected, I was wrong.
There is a potential race condition with the using statement that doesn't guarantee disposing and I've put together a console app illustrating this (which isn't hard or trivial).
I was correct when showing how the IL generates using like so:
var session = new Session(); //If this causes an error or abort happens during initialization then we don't enter try
//If abort is called here then we never enter try
//In either case above we may have undisposed resources initialized at this point
try
{
//do stuff
}
finally
{
session.Dispose();
}
However; note the comments where I show the race condition that may occur if aborted before entering try.
Here is a console app written just to prove this point. The first works as expected but if you add the commented out code //thread.Abort() when we initialize R then you will see it init but never dispose :/
using System;
using System.Threading;
namespace Question_Answer_Console_App
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Start Main");
Thread thread = null;
thread = new Thread(new ThreadStart(() =>
{
Console.WriteLine("Thread Started");
using (var r = new R(thread))
{
Console.WriteLine($"Using {nameof(R)}");
}
}));
thread.Start();
thread.Join();
Console.WriteLine("End Main");
Console.ReadKey();
}
}
public class R : IDisposable
{
public R(Thread thread)
{
Console.WriteLine($"Init {nameof(R)}");
//thread.Abort();
}
public void Dispose()
{
Console.WriteLine($"Disposed {nameof(R)}");
}
}
}
Output with //thread.Abort() commented out:
Start Main
Thread Started
Init R
Using R
Disposed R
End Main
Output with thread.Abort() not commented out:
Start Main
Thread Started
Init R
End Main

How to stop a thread if thread takes too long

I have a situation that i export data to a file and what i have been asked to do is to provide a cancel button which on click will stop the export if it takes too much time to export.
I started exporting to the file in a thread. And i try to abort the thread on the button click. But it do not work.
I searched on Google and i found that abort() is not recommended. But what else should I choose to achieve it?
My current code is:
private void ExportButtonClick(object param)
{
IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
DataTable dtData = ExportHelper.ToDataTable(data);
thread = new Thread(new ThreadStart(()=>ExportHelper.DataTableToCsv(dtData, "ExportFile.csv")));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Name = "PDF";
thread.Start();
}
private void StopButtonClick(object param)
{
if (thread.Name == "PDF")
{
thread.Interrupt();
thread.Abort();
}
}
Aborting a thread is a bad idea, especially when dealing with files. You won't have a chance to clean up half-written files or clean-up inconsistent state.
It won't harm the .NET Runtime bat it can hurt your own application eg if the worker method leaves global state, files or database records in an inconsistent state.
It's always preferable to use cooperative cancellation - the thread periodically checks a coordination construct like a ManualResetEvent or CancellationToken. You can't use a simple variable like a Boolean flag, as this can lead to race conditions, eg if two or more threads try to set it at the same time.
You can read about cancellation in .NET in the Cancellation in Managed Threads section of MSDN.
The CancellationToken/CancellationTokenSource classes were added in .NET 4 to make cancellation easier that passing around events.
In your case, you should modify your DataTableToCsv to accept a CancellationToken. That token is generated by a CancellationTokenSource class.
When you call CancellationTokenSource.Cancel the token's IsCancellationRequested property becomes true. Your DataTableToCsv method should check this flag periodically. If it's set, it should exit any loops, delete any inconsistent files etc.
Timeouts are directly supported with CancelAfter. Essentially, CancelAfter starts a timer that will fire Cancel when it expires.
Your code could look like this:
CancellationTokenSource _exportCts = null;
private void ExportButtonClick(object param)
{
IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
DataTable dtData = ExportHelper.ToDataTable(data);
_exportCts=new CancellationTokenSource();
var token=_exportCts.Token;
thread = new Thread(new ThreadStart(()=>
ExportHelper.DataTableToCsv(dtData, "ExportFile.csv",token)));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Name = "PDF";
_exportCts.CancelAfter(10000);
thread.Start();
}
private void StopButtonClick(object param)
{
if (_exportCts!=null)
{
_exportCts.Cancel();
}
}
DataTableToCsv should contain code similar to this:
foreach(var row in myTable)
{
if (token.IsCancellationRequested)
{
break;
}
//else continue with processing
var line=String.Join(",", row.ItemArray);
writer.WriteLine(line);
}
You can clean up your code quite a bit by using tasks instead of raw threads:
private async void ExportButtonClick(object param)
{
IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
DataTable dtData = ExportHelper.ToDataTable(data);
_exportCts=new CancellationTokenSource();
var token=_exportCts.Token;
_exportCts.CancelAfter(10000);
await Task.Run(()=> ExportHelper.DataTableToCsv(dtData, "ExportFile.csv",token)));
MessageBox.Show("Finished");
}
You could also speed it up by using asynchronous operations, eg to read data from the database or write to text files without blocking or using threads. Windows IO (both file and network) is asynchronous at the driver level. Methods like File.WriteLineAsync don't use threads to write to a file.
Your Export button handler could become :
private void ExportButtonClick(object param)
{
IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
DataTable dtData = ExportHelper.ToDataTable(data);
_exportCts=new CancellationTokenSource();
var token=_exportCts.Token;
_exportCts.CancelAfter(10000);
await Task.Run(async ()=> ExportHelper.DataTableToCsv(dtData, "ExportFile.csv",token)));
MessageBox.Show("Finished");
}
and DataTableToCsv :
public async Task DataTableToCsv(DataTable table, string file,CancellationToken token)
{
...
foreach(var row in myTable)
{
if (token.IsCancellationRequested)
{
break;
}
//else continue with processing
var line=String.Join(",", row.ItemArray);
await writer.WriteLineAsync(line);
}
You can use a boolean flag. Use a volatile boolean for that.
In the helper do something like:
this.aborted = false;
while(!finished && !aborted) {
//process one row
}
Whenever you want to cancel the operation, you call a method to set aborted to true:
public void Abort() {
this.aborted = true;
}
Have a read here: https://msdn.microsoft.com/en-us/library/system.threading.threadabortexception(v=vs.110).aspx
When a call is made to the Abort method to destroy a thread, the common language runtime throws a ThreadAbortException. ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block. When this exception is raised, the runtime executes all the finally blocks before ending the thread. Because the thread can do an unbounded computation in the finally blocks or call Thread.ResetAbort to cancel the abort, there is no guarantee that the thread will ever end. If you want to wait until the aborted thread has ended, you can call the Thread.Join method. Join is a blocking call that does not return until the thread actually stops executing.
Since Thread.Abort() is executed by another thread, it can happen anytime and when it happens ThreadAbortException is thrown on target thread.
Inside ExportHelper.DataTableToCsv:
catch(ThreadAbortException e) {
Thread.ResetAbort();
}
On StopButtonClick
if (thread.Name == "PDF")
{
thread.Interrupt();
thread.Join();
}
To Stop a thread you have one option of Thread.Abort.However because this method thrown ThreadAbortException on the target thread when it executed by another thead.
Which is not recommended.
The second option to stop a thread is by using shared variable that both your target and your calling thread can access.
See the Example ::
public static class Program
{
public static void ThreadMethod(object o)
{
for (int i = 0; i < (int)o; i++)
{
Console.WriteLine("ThreadProc: { 0}", i);
Thread.Sleep(0);
}
}
public static void Main()
{
bool stopped = false;
Thread t = new Thread(new ThreadStart(() =>
{
while (!stopped)
{
Console.WriteLine("Running...");
Thread.Sleep(1000);
}
}));
t.Start();
Console.WriteLine("Press any key to exit");
Console.ReadKey();
stopped = true;
t.Join();
}
}
//Source :: Book --> Programming in c#

Can I detect a hung process with another thread and recover from it?

I have a method that occasionally hangs (in a dll I cannot modify but must use). If I run it again It will typically work fine. I was wondering if it would be possible to make a background thread that would wait for 20 minutes and then throw an exception in my program.
var triesLeft = 5;
while (triesLeft > 0) {
try {
var t = new Thread(() => { wait(20 minutes); throw new ApplicationHungException();})
t.Start();
Object o = MethodThatHangsForever10PercentOfTheTime();
} catch (ApplicationHungException e) {
triesLeft--;
}
}
t.Abort();
This does not work because the exception does not pass to the try catch block it's contained in. Is there a way I can get the thread to give it's exception to the try catch block?
One way to do this would be set off your faulty method in the separate thread, and wait for one of two things to happen; either:
The thread completes, or
A predetermined amount of time (eg 20 mins) elapses
Once either of these things happens, we can take appropriate action.
The code would look something like this:
static void DoProcessing() {
var triesLeft = 5;
Object o = null;
while (triesLeft > 0) {
var t = new Thread(() => { o = MethodThatHangsForever10%OfTheTime(); }).Start();
if (t.Join(new TimeSpan(0, 20, 0))) {
// The thread completed
break;
} else {
// We're out of time.
t.Abort(); // Important: See comments below about this
triesLeft--;
}
}
}
It turns out that aborting threads is a risky and fragile operation, as pointed out by Reed Copsey in the comments below. Your alternatives are to allow the hung thread to live out the rest of its life (however long that may be), or to quarantine the Heisenbuggy method call to a separate process.
This opens up another can of worms however, since you would have to deal with interprocess communication, data serialization and synchronisation. This may or may not be worth it, which is a judgement call I leave to you.
You can do your work in a separate thread, and wait 20 minutes for it to complete:
var triesLeft = 5;
while (triesLeft > 0)
{
var mre = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(_ => {
MethodThatHangsForever10PercentOfTheTime();
mre.Set();
});
if (mre.WaitOne(TimeSpan.FromMinutes(20)))
{
break; // Success!
}
triesLeft--;
}
}

Must i abort this thread? Waiting for namedpipes. How do i do this properly?

I have another question about this same code and keeping the pipe open after the client closes it
But here i have a problem gracefully terminating my app. My main code is below. There are 2 problems. 1) I am using Thread.Abort and 2) This application doesnt actually end. I can set a breakpoint and see abort is called and step to the ending brace but the IDE is still in debug mode and the process is still alive (in process manager). How do i properly terminate this?
[STAThread]
static void Main(string[] args)
{
Thread t;
t = new Thread(new ThreadStart(ThreadStartServer));
bool hasInstance = true;
try
{
pipeStream = new NamedPipeServerStream(pipename);
hasInstance = false;
pipeStream.Close();
t.Start();
pipeStream.Dispose();
}
catch (System.IO.IOException)
{
hasInstance = true;
}
if (hasInstance)
{
clientPipeMessage(args[1]);
return;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
t.Abort();
}
static public void ThreadStartServer()
{
while (true)
{
using (NamedPipeServerStream pipeStream = new NamedPipeServerStream(pipename))
{
Console.WriteLine("[Server] Pipe created {0}", pipeStream.GetHashCode());
// Wait for a connection
pipeStream.WaitForConnection();
Console.WriteLine("[Server] Pipe connection established");
using (StreamReader sr = new StreamReader(pipeStream))
{
string temp;
while ((temp = sr.ReadLine()) != null)
{
Console.WriteLine("{0}: {1}", DateTime.Now, temp);
}
}
}
}
Console.WriteLine("Connection lost");
}
About Thread.Abort from MS documentation ... "Calling this method usually terminates the thread."
Furthermore "The thread is not guaranteed to abort immediately, or at all."
I suspect the WaitForConnection is blocking it from receiving the thread abort. Generally speaking, thread abort is considered Evil as who knows what state you could leave things in, etc. See here for some more help...http://www.interact-sw.co.uk/iangblog/2004/11/12/cancellation
As you suggest ... don't use Thread.Abort. Unless you have a very compelling reason why no other option will work it is a bad idea.
The problem is the blocking call to ReadLine ... so instead use StreamReader.Peek/Read to pull data from the named pipe. This will allow you to check a flag in the loop so that you can exit.
For a more complex solution you could use asynchronous I/O ... see this question for some pointers.
You need to "return" from your ThreadStartServer method when it has completed its work. If you combine this with a Join() in the Main method, the worker thread will finish gracefully. Additionally make it a BackGround thread. Here is an example (without the PipeStream):
class Prog
{
static void Main(string[] args)
{
Thread t;
t = new Thread(new ThreadStart(ThreadStartServer));
t.IsBackground = true;
try
{
t.Start();
// time consuming work here
}
catch (System.IO.IOException)
{
// from your example
}
t.Join();
}
static public void ThreadStartServer()
{
while (true)
{
int counter=0;
while (++counter < 10)
{
Console.WriteLine("working.");
// do time consuming things
Thread.Sleep(500);
}
return;
}
}
}

Categories