473,388 Members | 1,493 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,388 software developers and data experts.

Serious Threading.Monitor issues in .NET 2.0

Hi,

I have been looking into the claim that the keyword lock is not safe
when exceptions are possible. That lead me to try the following code,
which I think has uncovered a serious error in the .NET 2.0 framework.
Note that this runs better, but not perfectly, on .NET 1.1.

Note: The numbers are line numbers needed to match the callstack of the
exception below.

// Class WorkItem ...
34 public void DoItSafeLock()
35 {
36 try
37 {
38 Monitor.Enter(this);
39 Thread.Sleep(10);
40 }
41 finally
42 {
43 Monitor.Exit(this); // <-- SynchronizationLockException!
44 }
45 }

Here's what I get when I run this code on a different thread than the
one that created the object holding this method (WorkItem class):

System.Threading.SynchronizationLockException: Object synchronization
method was called from an unsynchronized block of code.
at WorkItems.WorkItem.DoItSafeLock() in
D:\LockTest\WorkItems\WorkItem.cs:line 43
at WorkItems.Worker.ExecuteWorkItem() in
D:\LockTest\WorkItems\Worker.cs:line 127

Notice that the error is that on line 43, the object does not hold a
lock on itself (this). How is this possible given that it made it
through the Monitor.Enter(this) call on line 38?!?

Does any one know what's going on here? How can the monitor be entered
and yet no lock is acquired?

I have a sample application demonstrating the error if any one wants to
see the code, it can be download here:

http://www.unitedbinary.com/TEMP-001...8/LockTest.zip

Thanks in advance,
Michael

Michael Kennedy

PS - This has been reposted to these newsgroups. It was originally
posted to the C# newsgroup only.

Nov 30 '05 #1
8 1692
Michael Kennedy wrote:
// Class WorkItem ...
34 public void DoItSafeLock()
35 {
36 try
37 {
38 Monitor.Enter(this);
39 Thread.Sleep(10);
40 }
41 finally
42 {
43 Monitor.Exit(this); // <-- SynchronizationLockException!
44 }
45 }

Here's what I get when I run this code on a different thread than the
one that created the object holding this method (WorkItem class):

System.Threading.SynchronizationLockException: Object synchronization
method was called from an unsynchronized block of code.
at WorkItems.WorkItem.DoItSafeLock() in
D:\LockTest\WorkItems\WorkItem.cs:line 43
at WorkItems.Worker.ExecuteWorkItem() in
D:\LockTest\WorkItems\Worker.cs:line 127

Notice that the error is that on line 43, the object does not hold a
lock on itself (this). How is this possible given that it made it
through the Monitor.Enter(this) call on line 38?!?


Note that the error message is that Exit "was called from an
unsynchronized block of code" - NOT that you do not have a lock on the
`this` reference. Note also that `lock (this) statement;` expands to

Monitor.Enter(this);
try {statement;}
finally {Monitor.Exit(this);}

NOT to

try {Monitor.Enter(this); statement;}
finally {Monitor.Exit(this);}

The difference is that the try statement in the three line version
(the lock expansion) is a synchronized block, while the try statement
in the two line statement is not. (The two line version will call Exit
even if Enter fails; the three line version will not.) I downloaded
your sample app and got the same exception (with 2.0, beta 2); when I
changed your WorkItem.DoItSafeLock to

public void DoItSafeLock()
{
Monitor.Enter(this);
try
{
//Monitor.Enter( this );
Thread.Sleep( 10 );
}
finally
{
Monitor.Exit( this );
}
}

I did not get an exception.

--
<http://www.midnightbeach.com>
Nov 30 '05 #2
Hi Jon,

Exactly, the "safeness" of it was to try to fix the inherent faults in
how lock behaves.

If there is an exception, it might leave this permanently locked.
Consider how lock expands:

lock(this)
{
// Do stuff
}

becomes:

Monitor.Enter(this);
// <-- Exception(e.g. threadabort), exit is never called after enter.
try
{
// Do stuff
}
finally
{
Monitor.Exit(this);
}

Notice that the program proves that lock() is unsafe. I was trying to
show that the try/enter/finally/leave method was in fact safe. But
instead I found the error that you also found.

Do you agree that it should not be an error?

Thanks,
Michael

Nov 30 '05 #3
Hi Jon,

[Sorry if this is a somewhat duplicated I think it might have not
posted correctly, so I'll try again]

Jon, I think I misunderstood you. You did not get an exception?

Did you look at the usage of lock() in the program? It proves that

lock(obj)

which is

Monitor.Enter(obj)
try {}
finally {Monitor.Leave(obj)}

Is definitely not safe. So if both try/enter/finally/leave and
enter/try/finally/leave is not safe, what do you suggest we use for
thread synch? Those are basically the two ways you could do it and this
program shows they are BOTH broken.

Thanks,
Michael

Nov 30 '05 #4
Michael Kennedy wrote:
Exactly, the "safeness" of it was to try to fix the inherent faults in
how lock behaves.
Imho, `lock` is fine. In general, you want to enter a state outside of
the try/finally block that exits the state, precisely so that you
don't try to undo what has never been done. Consider, for example,
locking `SomeRef` when SomeRef is null: You do NOT want to try to
acquire the monitor within the try block and release the monitor
within the finally block!
Monitor.Enter(this);
// <-- Exception(e.g. threadabort), exit is never called after enter.
try
{
// Do stuff
}
finally
{
Monitor.Exit(this);
}
Yes, this is why thread abort is no safer in managed code that in
unmanaged code.
Notice that the program proves that lock() is unsafe. I was trying to
show that the try/enter/finally/leave method was in fact safe. But
instead I found the error that you also found.

Do you agree that it should not be an error?


No, I think you found a PEBCAC error. The error message you got seems
exactly right.

--
<http://www.midnightbeach.com>
Nov 30 '05 #5
I suppose there is no way out of this eh?

The try/enter/finally/leave could prematurely unlock the code if you
have "doublly locked it"

try
{
<-- error here is a double leave
enter
try { enter }
finally {leave}
}
finally {leave}

and

// lock version
enter
<-- error here is a permently locked.
try
{
enter
try { }
finally {leave}
}
finally {leave}

Does ASP.NET only send thread abort exceptions from the main thread?

Thanks,
Michael

Nov 30 '05 #6

"Michael Kennedy" <mk******@unitedbinary.com> wrote in message
news:11**********************@g43g2000cwa.googlegr oups.com...
Hi Jon,

[Sorry if this is a somewhat duplicated I think it might have not
posted correctly, so I'll try again]

Jon, I think I misunderstood you. You did not get an exception?

Did you look at the usage of lock() in the program? It proves that

lock(obj)

which is

Monitor.Enter(obj)
try {}
finally {Monitor.Leave(obj)}

Is definitely not safe. So if both try/enter/finally/leave and
enter/try/finally/leave is not safe, what do you suggest we use for
thread synch? Those are basically the two ways you could do it and this
program shows they are BOTH broken.

Thanks,
Michael


As I replied in the csharp NG (pease don't multipost for that reason), what
is unsafe, is calling Thread.Abort from another thread (asynchronous thread
aborts are a NO NO in .NET), if you do call Thread.Abort to terminate
another running thread, you should regard your Application Domain as doomed
and you should unload it.

Note however, that "lock" as been made more reliable in the face of Thread
Aborts resulting from AD unloads in V2 of the FW.
In this version, the JIT (actually a hack) doesn't condider the window
between the lock aquisition and entering the protected block. That means
that the lock will only be released when effectively aquired. But, that
doesn't mean you can call thread abort from user code and continue to
execute in the doomed AD.

Willy.


Nov 30 '05 #7
Jon,

Sorry I missed this point:
The two line version will call Exit
even if Enter fails; the three line version will not.


When will Monitor.Enter fail?

Also, you may need to run it several times to get the second part to
fail. Basically your rewrite (via using lock(this)). You will get
errors, they are just rare race conditions. See the SampleOutput.txt
file in the project.

Finally, if try/enter/finally/leave is not acceptible and lock() {}
clearly is not acceptible, then what other options do we have for
synchronization? Neither work. What can we do?

Thanks,
Michael

Nov 30 '05 #8
Michael Kennedy wrote:
When will Monitor.Enter fail?
When you passs it null. Or, perhaps, in the face of an out-of-memory
situation (if lock() needs to create a new synch block, and can't).
Finally, if try/enter/finally/leave is not acceptible and lock() {}
clearly is not acceptible, then what other options do we have for
synchronization? Neither work. What can we do?


Only call Abort() when you're aborting the whole app.

--
<http://www.midnightbeach.com>
Dec 1 '05 #9

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

77
by: Jon Skeet [C# MVP] | last post by:
Please excuse the cross-post - I'm pretty sure I've had interest in the article on all the groups this is posted to. I've finally managed to finish my article on multi-threading - at least for...
1
by: Peter Schmitz | last post by:
Hi, I just tried to convert my current project to the new beta 2 of the .net framework and I encountered some problems with my thread synchronization - that formerly worked well. When I...
21
by: Illumineo | last post by:
My application uses multiple-threads and is a kind of AI simulation or ALife. This works fine but I was wondering if and how one can make use of hyper-threading in C#? Thanks for any hint,...
15
by: aikwee | last post by:
hi all, i have a software written in vb.net which has many thread in it. The software basically use about 8 threads to monitor in coming data from serial port, and some other thread that...
4
by: Bob | last post by:
- For cleanup, is it sufficient to set a Thread to Nothing after it's done? - It is OK to pass objects out of the thread? (dumb question maybe but I want to be sure) - What's the best way to...
25
by: Michael Kennedy | last post by:
Hi, I have been looking into the claim that the keyword lock is not safe when exceptions are possible. That lead me to try the following code, which I think has uncovered a serious error in the...
1
by: abhra.haldar | last post by:
Hi: I am new to multithreaded applications, and was having some problems coordinating a few threads. Here is the code I use: >>>> ContextStorage cs = new...
2
by: WXS | last post by:
When I see things in .NET 2.0 like obsoletion of suspend/resume because of the public reason MS gives of they think people are using them inappropriately.. use mutex, monitor and other...
15
by: WXS | last post by:
When I see things in .NET 2.0 like obsoletion of suspend/resume because of the public reason MS gives of they think people are using them inappropriately.. use mutex, monitor and other...
126
by: Dann Corbit | last post by:
Rather than create a new way of doing things: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2497.html why not just pick up ACE into the existing standard:...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.