Does a Locked Object Stay Locked If an Exception Occurs Inside It

Does a locked object stay locked if an exception occurs inside it?

First; have you considered TryParse?

in li;
if(int.TryParse(LclClass.SomeString, out li)) {
// li is now assigned
} else {
// input string is dodgy
}

The lock will be released for 2 reasons; first, lock is essentially:

Monitor.Enter(lockObj);
try {
// ...
} finally {
Monitor.Exit(lockObj);
}

Second; you catch and don't re-throw the inner exception, so the lock never actually sees an exception. Of course, you are holding the lock for the duration of a MessageBox, which might be a problem.

So it will be released in all but the most fatal catastrophic unrecoverable exceptions.

Exceptions inside the lock block

Lock will be released when exception escapes from the lock block.

That is because lock(){...} is translate by compiler roughly into:

Monitor.Enter(obj);
try{

// contents of the lock block

}finally{
Monitor.Exit(obj);
}

Lock statement - does it always release the lock?

In 4.0 implementation if a thread abort or any cross thread exception occurs just before the Monitor.Exit(temp) in the finally block is executed - would that keep the lock on the object?

Let's take a look at that code, so that it is clear to other readers:

bool lockWasTaken = false;
var temp = obj;
try
{
Monitor.Enter(temp, ref lockWasTaken);
{
body
}
}
finally
{
if (lockWasTaken)
{
// What if a thread abort happens right here?
Monitor.Exit(temp);
}
}

Your question is not answerable because it is based on a false assumption, namely, that thread aborts can happen in the middle of a finally block.

Thread aborts cannot happen in the middle of a finally block. That is just one reason amongst many reason why you should never attempt to abort a thread. The entire thread could be running in a finally block, and therefore be not-abortable.

Is there any possibility for an exception to occur at this level, leaving the object still in a locked state?

No. A thread abort will be delayed until control leaves the finally. Unlocking a valid lock does not allocate memory or throw another exception.

How long will a C# lock wait, and what if the code crashes during the lock?

When another thread comes by and wants to execute the code, how long will it wait until the lock is released?

lock will block the the thread trying to enter the lock indefinitely until the object being locked on is released.

can you somehow set a timeout?

If you need to specify a timeout, use Monitor.TryEnter as in

if(Monitor.TryEnter(obj, new TimeSpan(0, 0, 1))) {
try {
body
}
finally {
Monitor.Exit(obj);
}
}

if the DoIt() method throws an exception, is the lock still released?

Yes, a lock(obj) { body } is translated to:

bool lockWasTaken = false;
var temp = obj;
try { Monitor.Enter(temp, ref lockWasTaken); { body } }
finally { if (lockWasTaken) Monitor.Exit(temp); }

For the gory details on what can happen when an exception is thrown, see Locks and exceptions do not mix.

What happens when a thread crashes holding a lock(SyncObject)?

lock(obj) { ... } is just a syntactic sugar for

Monitor.Enter(obj);
try
{ ... }
finally
{
Monitor.Exit(obj);
}

and in C# 4.0 it was changed to

bool acquaired = false;
try
{
Monitor.Enter(obj, ref acquired);
...
}
finally
{
if (acquired)
{
Monitor.Exit(obj)
}
}

Now, prior to .Net 4.0, it was possible that ThreadAbortException would be thrown between the Monitor.Enter(obj); and the beginning of the try block - and that would've created a dead lock (which means, no thread can access the lock, and they would remain blocked). Now even this is impossible. Anyway, event if an exception was thrown inside the lock, the lock would be released because of the finally block.

** I believe @Jon Skeet deserve credit for this.

Nested lock in Task.ContinueWith - Safe, or playing with fire?

No it will not burn you. Even if the ContinueWith is inlined into to the current thread that was running the new Task(() => new DatafeedUploadHandler().. it will get the lock e.g. no dead lock.
The lock statement is using the Monitor class internally, and it is reentrant. e.g. a thread can aquire a lock multiple times if it already got/owns the lock. Multithreading and Locking (Thread-Safe operations)

And the other case where the task.ContinueWith starts before the ProcessModifiedDatafeed finished is like you said. The thread that is running the ContinueWith simply would have to wait to get the lock.

I would really consider to do the task.ContinueWith and the task.Start() outside of the lock if you reviewed it. And it is possible based on your posted code.

You should also take a look at the ConcurrentDictionary in the System.Collections.Concurrent namespace. It would make the code easier and you dont have to manage the locking yourself. You are doing some kind of compare exchange/update here if (this.fileWatcherDictionary.ContainsNonNullKey(eventArgs.FullPath)). e.g. only add if not already in the dictionary. This is one atomic operation. There is no function to do this with a ConcurrentDictionary but there is an AddOrUpdate method. Maybe you can rewrite it by using this method. And based on your code you could safely use the ConcurrentDictionary at least for the runningTaskDictionary

Oh and TaskCreationOptions.LongRunning is literally creating a new thread for every task which is kind of an expensive operation. The windows internal thread pool is intelligent in new windows versions and is adapting dynamically. It will "see" that you are doing lots of IO stuff and will spawn new threads as needed and practical.

Greetings

locking a resource via lock within try. Is it wrong?

I need to deal with the fact that the code within that lock block can throw exception

And there's your problem. That's a terrible situation to be in.

Why are you locking in the first place? Usually the reason why you lock something is because you want to implement the following logic:

  • lock the door
  • make a mess
  • clean it up
  • unlock the door

If you do that, then no one who honours the locked door ever sees the mess.

For example, you might want to swap values of variables "left" and "right" in a threadsafe manner, so you:

  • take the lock
  • read the left variable into tempLeft
  • read the right variable into tempRight
  • write tempLeft into right
  • we just made a mess; the original value of 'right' has gone missing
  • write tempRight into left
  • we've cleaned up the mess, all is well with the world again
  • release the lock

Now suppose an exception is thrown after the mess is made. What happens? We jump straight to the unlock, leaving the mess for another thread to see.

That's why you should never throw an exception inside a lock; it completely defeats the purpose of the lock! The whole point of a lock is to ensure that state is always observed to be consistent by all threads except the one responsible for cleaning up the mess.

If you have an exception that can be thrown from inside a lock, the best thing to do is to get out of that horrible situation. If you can't do that, then make sure that you can either (1) destroy the process utterly as soon as the exception escapes the lock, so that the mess you made cannot cause data loss or other harm -- do a FailFast and nuke the process from orbit, it's the only way to be sure -- or (2) write rollback code that undoes whatever operation you were attempting before the lock is exited; that is, clean up the mess back to the original state.

If the latter is your strategy then don't put the try block outside the lock; it's useless there because the instant control leaves the lock via the exception another thread can be crashing and dying because of the mess you left exposed to it. Put the try that deals with the exception inside the lock:

lock(whatever)
{
try
{
MakeAMess();
}
finally
{
CleanItUp();
// Either by completing the operation or rolling it back
// to the pre-mess state
}
}

If you have strong reliability requirements then dealing with locked critical sections which can throw exceptions is an extremely difficult programming task best left to experts; you might consider using a constrained execution region if you find yourself in this situation a lot.



Related Topics



Leave a reply



Submit