Dejvovo
Dejvovo

Reputation: 129

After EnterWriteLock the lock is not held

I have a AVL tree data structure, where every node has its own lock. Its because there is more writers which are trying to acces a node.

class Node
{
    public ReaderWriterLockSlim ww;
    // ...
    public Node()
    {
        ww = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
        // ...
    }
}
class AVL_tree
{
    public Node root;
    // ...
    public void Write(int value)
    {
        root = new Node();
        root.ww.EnterWriteLock();
        if (!root.ww.IsWriteLockHeld) throw new Exception("Why?");
        // ...
        root.ww.ExitWriteLock();
    }
}

Each writer is starting in new thread

class Program{
    public static AVL_Tree data;
    static void Main()
    {
        data = new AVL_Tree();
        List<Thread> vlakna = new List<Thread>();
        for (int i = 1; i < 10; i++)
            vlakna.Add(new Thread(Write));
        foreach (Thread vlakno in vlakna)
            vlakno.Start();
    }
    public static void Write() // Write some random data into the tree
    {
        Random rnd = new Random(DateTime.Now.Millisecond);
        data.Writer(rnd.Next(1, 999));
    }

Writer doesn't look exactly like this, there is more nodes and more of code, but the problem is following:

After locking the node, the lock is not held, sometimes. I don't understand why. Have anyone some kind of explanation.

*Sometimes means that I can't find out when it is going to happend.

Upvotes: 1

Views: 714

Answers (1)

Matthew Watson
Matthew Watson

Reputation: 109537

Are you sure it is being accessed by a different thread? If it is the same thread recursively acquiring the lock, it will be granted to every recursion level.

To see if this is the case, change the lock recursion policy to NoRecursion and see if you get an exception.

[EDIT]

Here's another thought: You have a race condition.

Each thread you start is calling data.Write(), i.e. AVL_tree.Write().

Inside AVL_tree.Write() you assign a new root node.

Let's examine your AVL_tree.Write():

class AVL_tree
{
    public Node root;
    // ...
    public void Write(int value)
    {
        root = new Node();         // [A]
        root.ww.EnterWriteLock();  // [B]
        if (!root.ww.IsWriteLockHeld) throw new Exception("Why?"); // [C]
        // ...
        root.ww.ExitWriteLock();
    }
}

Imagine that thread 1 has executed up to line [B], and is ABOUT TO execute line [C].

Now imagine that thread 2 comes along and executes line [A] and is ABOUT TO execute line [B].

At this point, the root field has been overwritten by a new one, one that has NOT yet acquired its write lock.

Now imagine that thread 1 continues to line [C]. It looks at the root (now the one that was newed by thread 2), and discovers that the write lock is not held and therefore throws an exception.

That is what I think is happening.

Upvotes: 3

Related Questions