Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Corrupted lock ? Magic deadlock?

I work with multethreading bug. Now I see that for some reason lock isn't executed even once but is locked. I have the next class:

public sealed class Foo
{
       private readonly object _lock = new object();
       private static ulong _inCnt = 0;

    public void SomeMethod(ulong poo)
    {
        lock (_lock)
        {
           _inCnt++;
           ... [some code]
        }
    }
 }

I paused all threads in the VS, inspected all of them and see that there is only one thread in the SomeMethod and it is waiting for lock (_lock) to be freed (_inCnt = 0). I resumed threads, waited for a while, paused threads and see the same picture, the same (and only one) thread is still waiting for the lock (_lock) in SomeMethod and _inCnt is zero! But it will be one or more if lock will be ever entred(_inCnt++ is the first line after lock (_lock) no exception can happens, we don't abort threads). How can it be zero and lock is locked?

like image 696
Brans Ds Avatar asked Dec 03 '13 07:12

Brans Ds


People also ask

How do you fix Ora-00060 deadlock detected while waiting for resources?

Action: Look at the trace file to see the transactions and resources involved. Retry if necessary. There are several causes for this error: Too high activity - Re-running the job during a less busy period can fix this ORA-00060 deadlock error.

How to avoid deadlock in Oracle?

LOCK IN SHARE MODE ), try using a lower isolation level such as READ COMMITTED . When modifying multiple tables within a transaction, or different sets of rows in the same table, do those operations in a consistent order each time. Then transactions form well-defined queues and do not deadlock.


1 Answers

If all of your assumptions are correct, and you are really sure that there never was an unexpected thread abort, then you have to consider GC heap data corruption. The field in System.Object that stores the lock state is fairly vulnerable, it is the very first field in the object. So even a modest buffer overrun in pinvoked native code is liable to overwrite that field and make the CLR think that the lock is held.

Assumptions are however the mother of undebuggable problems and unanswerable questions. Best to check them, it is actually debuggable. I'll assume 32-bit code execution. Use Debug + QuickWatch and type &_lock. That gives you the address of the object reference. Switch to Debug + Windows + Memory + Memory1 and type the address you got. Right-click the window and select "4-byte integer". You'll now see the address of the object, where it is stored in the GC heap. Subtract 4 from that number and type the result in the Address box. You now see the field that stores the lock state. It is 0 if the lock isn't held, if it is held then it contains the Thread.ManagedId of the thread that owns the lock. You can correlate that to the Debug + Windows + Threads window.

Three basic scenarios:

  • If you find the thread back in the Threads window then you've got a deadlock, you'll be highly interested in what that thread is doing. Double-click it and look at the Call Stack window to see why it isn't making progress
  • If you can't find that thread back then you have a very strong hint that your code is suffering from the "unexpected thread abort" mishap
  • If you see a bizarre random number then you've got the GC heap corruption scenario.
like image 164
Hans Passant Avatar answered Sep 21 '22 05:09

Hans Passant