Coder Social home page Coder Social logo

neosmart / asynclock Goto Github PK

View Code? Open in Web Editor NEW
186.0 186.0 29.0 69 KB

An async/await-friendly lock for .NET, complete with asynchronous waits, safe reëntrance, and more.

Home Page: https://neosmart.net/blog/2017/asynclock-an-asyncawait-friendly-locking-library-for-c-and-net/

License: MIT License

C# 97.59% Shell 2.41%
async await c-sharp c-sharp-library nuget-package synchronization

asynclock's People

Contributors

danielchalmers avatar dyarkovoy avatar mqudsi avatar stroniax avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

asynclock's Issues

Wrapping reentrant call in a loop causes deadlock

I have taken the sample from the "AsyncLock usage example" section of the documentation and added a for loop around the inner reentrant call and observed a deadlock occur

This is the sample code I used

using NeoSmart.AsyncLock;
using System.Threading.Tasks;
using System;
using System.Threading;

public class AsyncLockTest
{
    static async Task Main(string[] args)
    {
        new AsyncLockTest().Test();
        Thread.Sleep(Timeout.Infinite);
    }
    AsyncLock _lock = new AsyncLock();

    void Test()
    {
        // The code below will be run immediately (likely in a new thread)
        Task.Run(async () =>
        {
            // A first call to LockAsync() will obtain the lock without blocking
            using (await _lock.LockAsync())
            {
                // A second call to LockAsync() will be recognized as being
                // reentrant and permitted to go through without blocking.

                for(int i=0;i<100;++i)
                {
                    Console.WriteLine($"Starting iteration {i} from {Thread.CurrentThread.ManagedThreadId}");
                    using (await _lock.LockAsync())
                    {
                        Console.WriteLine($"Obtained lock for iteration {i} from {Thread.CurrentThread.ManagedThreadId}");
                        // We now exclusively hold the lock for 1 minute
                        await Task.Delay(TimeSpan.FromMilliseconds(1));
                        Console.WriteLine($"Finished work for iteration {i} from {Thread.CurrentThread.ManagedThreadId}");
                    }
                    Console.WriteLine($"Released lock for iteration {i} from {Thread.CurrentThread.ManagedThreadId}");
                }
            }
        }).Wait(TimeSpan.FromSeconds(30));

        // This call to obtain the lock is made synchronously from the main thread.
        // It will, however, block until the asynchronous code which obtained the lock
        // above finishes.
        using (_lock.Lock())
        {
            // Now we have obtained exclusive access.
            // <Safely perform non-thread-safe operation safely here>
        }
    }
}

AsyncLock and DispatcherTimer

Hi, I am using the AsyncLock and I have observed that it does not play well in the following scenario:

I have a long running async operation, triggered from the app's UI thread. The app also has a DispatcherTimer which updates status and runs frequently. To prevent cross thread issues, I have added the AsyncLock. But it does not work: I can see that the method called by the DispatcherTimer can successfully acquire the lock, although it is also held by the long running operation.

Have you seen something like this? I assume that the AsyncLock might falsely assume reentrancy, because the DispatcherTimer call comes from the same thread as the original async operation. But of course, there is no reentrancy here. The DispatcherTimer method should block until the long running operation is completed.

There does not seem to be a way to disable reentrancy for your AsyncLock, right? What do you think of the observed behavior?

Support cancellation token for synchronous lock method

Since the synchronous Lock method is calling _parent.Wait(), which accepts a cancellation token, please allow a cancellation token to be provided to the Lock() method.

internal IDisposable ObtainLock()
{
while (!TryEnter())
{
// We need to wait for someone to leave the lock before trying again.
_parent._retry.Wait();
}
return this;
}

 internal IDisposable ObtainLock(CancellationToken cancellationToken) 
 { 
     while (!TryEnter()) 
     {
         // We need to wait for someone to leave the lock before trying again. 
         _parent._retry.Wait(cancellationToken); 
     }
     return this; 
 } 

public IDisposable Lock()
{
var @lock = new InnerLock(this, _asyncId.Value, ThreadId);
// Increment the async stack counter to prevent a child task from getting
// the lock at the same time as a child thread.
_asyncId.Value = Interlocked.Increment(ref AsyncLock.AsyncStackCounter);
return @lock.ObtainLock();
}

public IDisposable Lock(CancellationToken cancellationToken = default) 
{ 
    var @lock = new InnerLock(this, _asyncId.Value, ThreadId); 
    // Increment the async stack counter to prevent a child task from getting 
     // the lock at the same time as a child thread. 
    _asyncId.Value = Interlocked.Increment(ref AsyncLock.AsyncStackCounter); 
    return @lock.ObtainLock(cancellationToken); 
} 

I haven't looked very much at the code, I imagine proper disposal may also be required if the cancellation is requested.

Async locking based on key

Sometimes, I have situations where I want to lock certain areas for a particular user only (e.g. some business logic must be run in one piece for a certain user). I'm using neosmart AsyncLock in some software solutions and therefore - instead of inventing some new nugets - I was asking myself if it would be possible to extend AsyncLock.LockAsync method with a key parameter.

using (await _lock.LockAsync(userId))
{
   // Protected for User with userId
}

I'm not sure if you let other people touch your code (you know what I mean...) and I also have not a very clear picture of how I could integrate such a feature into AsyncLock. But I guess we could use some kind of thread-safe dictionary, which adds key-value pairs of T (generic-typed key) and value (the lock handle, some SemaphoreSlim or whatever you use in AsyncLock).

Let me know what you think about it.

Nuget Package NeoSmart.AsyncLock bug

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestCore
{
    class TestAsyncQueue
    {
        AsyncQueue<string> asyncQueue = new AsyncQueue<string>();

        public void Test()
        {
            asyncQueue.Handler = DoProcess;

            for (int i = 0; i < 10; i++)
            {
                asyncQueue.Enqueue(i.ToString());
            }


        }
        private async Task DoProcess(string data)
        {
            await Task.Delay(1000);
            Console.WriteLine(DateTime.Now + "   " + data);
        }

    }

    public class AsyncQueue<T>
    {
        //private readonly Nito.AsyncEx.AsyncLock _lock = new Nito.AsyncEx.AsyncLock();
        private readonly NeoSmart.AsyncLock.AsyncLock _lock = new NeoSmart.AsyncLock.AsyncLock();

        public Func<T, Task> Handler { get; set; } = (t) => Task.CompletedTask;


        public async void Enqueue(T item)
        {
            using (await _lock.LockAsync())
            {         
                Console.WriteLine("start task " + item);
                await Handler.Invoke(item);
                Console.WriteLine("end task" + item);
            }            
        }
    }
}

image

version : 0.3.0.2

AsyncLock

I feel that async locking with thread re-entrancy should be a built-in feature of the language. If you are interested in discussion please see the official language forum at dotnet/csharplang#983.

Nested async lock and parallel execution issue

Hi!

I've written quite stupid check for nested async locking and parallel execution (and forgot to move asyncLock out of method!) and got deadlock after 1-2 stepping out of nested "Lock2"
upd: even after making asyncLock static readonly - issue still persist

static void Main(string[] args)
        {
            try
            {
                Task.WhenAll(Enumerable.Range(0, 10).Select(SomeMethod)).GetAwaiter().GetResult();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }

private static async Task SomeMethod(int i)
        {
            var asyncLock = new AsyncLock();
            Console.WriteLine($"Outside {i}");
            await Task.Delay(100);
            using (await asyncLock.LockAsync())
            {
                Console.WriteLine($"Lock1 {i}");
                await Task.Delay(100);
                using (await asyncLock.LockAsync())
                {
                    Console.WriteLine($"Lock2 {i}");
                    await Task.Delay(100);
                }
            }
        }

Is it an issue, or I'm doing something wrong?

Reentrance deadlock

This code deadlocks:

https://dotnetfiddle.net/CkK674

Actually that test has been a challenge for every single async lock I currently know of, so I made my own async lock that passes. I'm not trying to plug for my own stuff on your GitHub, I'm just trying to show that it is possible and offer some direction toward a fix. Let me know if you'd like to discuss how my async lock works.

can't use in async void method.

    private AsyncLock lll = new AsyncLock();

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
      using (var l = lll.Lock())
      {
        await Task.Delay(5000);
        Trace.TraceInformation("Button_Click delay 5000");
      }
    }

Strong-named assemblies

Would it be possible for you to provide strong-named (signed) assemblies in your nuget package?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.