Skip to content

Bug: SemaphoreSlim being released without entering #185

@EricSites

Description

@EricSites

Describe the bug

at line

await _semaphoreConnect.WaitAsync(cancellationToken).ConfigureAwait(false);

internal async Task InternalConnectAsync(
        bool requireInitialized,
        CancellationToken cancellationToken
    )
    {
        if (!_wsClient.IsStarted || (requireInitialized && !_isInitialized))
        {
            try
            {
                await _semaphoreConnect.WaitAsync(cancellationToken).ConfigureAwait(false);

                if (!_wsClient.IsStarted)
                {
                    await Connect(cancellationToken).ConfigureAwait(false);
                }
            }
            finally
            {
                _semaphoreConnect.Release();
            }
        }
    }

The line await _semaphoreConnect.WaitAsync(cancellationToken).ConfigureAwait(false); should be outside of the try.

The cancellationToken token can be canceled without entering the semaphore, but the finally clause would still be called causing the semaphore to go negative.

Documentation on this here: https://learn.microsoft.com/en-us/dotnet/api/system.threading.semaphoreslim.wait?view=net-9.0#system-threading-semaphoreslim-wait(system-threading-cancellationtoken)

The code should look like:

internal async Task InternalConnectAsync(
        bool requireInitialized,
        CancellationToken cancellationToken
    )
    {
        if (!_wsClient.IsStarted || (requireInitialized && !_isInitialized))
        {
            await _semaphoreConnect.WaitAsync(cancellationToken).ConfigureAwait(false);
            try
            {
                if (!_wsClient.IsStarted)
                {
                    await Connect(cancellationToken).ConfigureAwait(false);
                }
            }
            finally
            {
                _semaphoreConnect.Release();
            }
        }
    }

Steps to reproduce

Drop the connection which will cause the cancellationToken to trigger after a long idle period.

Expected behaviour

No more logs like so:
System.Threading.SemaphoreFullException: Adding the specified count to the semaphore would cause it to exceed its maximum count.

exception_stacktrace:System.Threading.SemaphoreFullException: Adding the specified count to the semaphore would cause it to exceed its maximum count.
   at System.Threading.SemaphoreSlim.Release(Int32 releaseCount)
   at SurrealDb.Net.Internals.SurrealDbWsEngine.InternalConnectAsync(Boolean requireInitialized, CancellationToken cancellationToken) in /build/source/SurrealDb.Net/Internals/SurrealDbEngine.Ws.cs:line 1428
   at SurrealDb.Net.Internals.SurrealDbWsEngine.SendRequestAsync(String method, Object[] parameters, SurrealDbWsRequestPriority priority, CancellationToken cancellationToken) in /build/source/SurrealDb.Net/Internals/SurrealDbEngine.Ws.cs:line 1449
   at SurrealDb.Net.Internals.SurrealDbWsEngine.Select[T](RecordId recordId, CancellationToken cancellationToken) in /build/source/SurrealDb.Net/Internals/SurrealDbEngine.Ws.cs:line 871```

### SurrealDB version

SurrealDB 2.2.1

### Package version(s)

latest

### Contact Details

esites@calamu.com

### Is there an existing issue for this?

- [x] I have searched the existing issues

### Code of Conduct

- [x] I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions