Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.
This repository was archived by the owner on Nov 1, 2020. It is now read-only.

Odd threading behavior - Thread.CurrentThread.ManagedThreadId != Environment.ManagedThreadId #6041

@christianscheuer

Description

@christianscheuer

I'm getting a The write lock is being released without being held exception when exiting write mode on a ReaderWriterLockSlim on macOS.

Debugging this issue has led me to conclude, that apparently my call toExitWriteLock happens on a different thread than where I started, albeit in a weird way.

When I debug it, Thread.CurrentThread.ManagedThreadId will indicate I'm still on thread 1 where I acquired the lock, but Environment.ManagedThreadId which is what ReaderWriterLockSlim uses, indicates I'm on thread 6.
This does not seem to happen on CoreCLR.

The code paths involved do not include any calls to Thread.Start or asynchronous Task code. It really all should be happening on the same thread (which Thread.CurrentThread.ManagedThreadId also seems to indicate is true).
The rest of the app does have asynchronous Task code though.

Unfortunately, I haven't yet been able to make a repro case. It does seem to make a difference though if GC is invoked between the calls to EnterWriteLock and ExitWriteLock.
Any ideas as how to better debug this?

This is basically what I'm doing, but I don't think this helps much.

using System;
using System.Collections.Generic;
using System.Threading;

namespace Test
{

    public struct WriteLock : IDisposable
    {
        private readonly LockManager lockManager;

        internal WriteLock(LockManager lockManager)
        {
            this.lockManager = lockManager;
        }

        public void Dispose()
        {
            this.lockManager.ReleaseWriteLock();
        }
    }

    public class LockManager : IDisposable
    {
        private ReaderWriterLockSlim readerWriterLock;

        public LockManager()
        {
            this.readerWriterLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
        }

        public IDisposable AcquireWriteLock()
        {
            // Enter the lock
            if (!this.readerWriterLock.TryEnterWriteLock(-1))
            {
                throw new Exception("Could not enter write lock");
            }
            Console.WriteLine("Entered write lock: " + Thread.CurrentThread.ManagedThreadId);

            // Return a new instance of a write lock
            return new WriteLock(this);
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        internal void ReleaseReadLock()
        {
            this.readerWriterLock.ExitReadLock();
        }

        internal void ReleaseWriteLock()
        {
            this.readerWriterLock.ExitWriteLock();
        }

        protected virtual void Dispose(bool disposing)
        {
            Console.WriteLine("Disposing lock manager... disposing: " + disposing);
            if (disposing)
            {
                if (this.readerWriterLock != null)
                {
                    this.readerWriterLock.Dispose();
                    this.readerWriterLock = null;
                }
            }
        }   
  

        public static void Main(string[] args)
        {
            using (var lockMan = new LockManager())
            using (var locky = lockMan.AcquireWriteLock())
            {
                //Lots of stuff happening in here. No Task awaits though...
            }
        }
    }

}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions