0
\$\begingroup\$

I'm wrote Asynchronous Lock provider for reducing lock that breaks async keyword.

All tasks with async and await keywords are chained by GetAwaiter().OnCompleted(Callback) on I know. But some cases are not, for example: lock keyword, EventObject.Wait(), Mutex like objects... etc.

So, I think that if asynchronous chainable lock is possible, it can improve async performance.

/// <summary>
/// Asynchronous Lock Provider.
/// </summary>
public class AsyncLock
{
    private Task m_Current = null;

    /// <summary>
    /// Wait the lock asynchronously.
    /// </summary>
    /// <returns></returns>
    public async Task<IDisposable> WaitAsync()
    {
        var Current = new TaskCompletionSource<bool>();

        while (true)
        {
            Task Latest = null;

            lock (this)
            {
                if (m_Current is null || m_Current.IsCompleted)
                {
                    m_Current = Current.Task;
                    break;
                }

                Latest = m_Current;
            }

            await Latest;
        }

        return new ReleaseOnDispose(() => Current.TrySetResult(true));
    }

    /// <summary>
    /// Release on Dispose.
    /// </summary>
    private struct ReleaseOnDispose : IDisposable
    {
        private Action m_Release;

        /// <summary>
        /// Construct a new ReleaseAsync structure using Release functor.
        /// </summary>
        /// <param name="Release"></param>
        public ReleaseOnDispose(Action Release)
            => m_Release = Release;

        /// <summary>
        /// Release the Lock on dispose.
        /// </summary>
        public void Dispose() => m_Release?.Invoke();
    }
}

Usage:

/// <summary>
/// Read bytes with buffered.
/// </summary>
/// <param name="Length"></param>
/// <returns></returns>
public async Task<byte[]> ReadAsync(int Length)
{
    using (await m_ReadLock.WaitAsync())
    {
        byte[] Output = m_Buffer.Take(Length);

        if (Output.Length <= 0)
        {
            int Read = await m_Link.ReadAsync(new ArraySegment<byte>(m_ByteBuffer));
            if (Read <= 0)
            {
                await m_Link.CloseAsync();
                return null;
            }

            if (Read > Length)
            {
                m_Buffer.Append(m_ByteBuffer, Length, Read - Length);
                Read = Length;
            }

            Array.Resize(ref Output, Read);
            Array.Copy(m_ByteBuffer, 0, Output, 0, Read);
        }

        return Output;
    }
}
  1. Is this really better than traditionally used locks?
  2. Can my code be accepted on commercial development?
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Did I answer your question? \$\endgroup\$
    – aepot
    Commented Jun 17, 2021 at 20:53

1 Answer 1

2
\$\begingroup\$

SemaphoreSlim provides async locking out-of-the-box.

using var semaphore = new SemaphoreSlim(1);

// ...

await semaphore.WaitAsync();
try
{
    // critical section code
}
finally
{
    semaphore.Release();
}

Is this really better than traditionally used locks?

Nope. Because you're just wrapping monitors with new Task instance spawn. It could be inefficient in performance.

Can my code be accepted on commercial development?

There's no sense in general to use the provided solution as .NET provides already existent and well-implemented one.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.