You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi and sorry for the relatively low effort bug report. (which I think is a bug, in how it is analyzed?)
I think through the way Coyote is executing the test it produces the following error (which never occurs when executing the code normally):
The active test run was aborted. Reason: Test host process crashed : Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.Coyote.Rewriting.Types.Threading.Monitor.SynchronizedBlock.EnterLock()
at Microsoft.Coyote.Rewriting.Types.Threading.Monitor.SynchronizedBlock.Lock(Object syncObject)
at Microsoft.Coyote.Rewriting.Types.Threading.Monitor.Enter(Object obj, Boolean& lockTaken)
at SVN.MacTool.LdapBase.Connect.LdapConnectionPool.CleanupPool(Object state) in C:\Develop\TFS\SVN.MacTool\src\SVN.MacTool.LdapBase\Connect\LdapConnectionPool.cs:line 179
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.TimerQueueTimer.Fire(Boolean isThreadPool)
at System.Threading.TimerQueue.FireNextTimers()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
Test Run Aborted.
When testing this class
using System.Collections.Concurrent;
using System.DirectoryServices.Protocols;
using System.Net;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Logging;
using SVN.MacTool.Common.EventBus;
using SVN.MacTool.Common.EventBus.Messages;
using SVN.MacTool.LdapBase.Configuration;
namespace SVN.MacTool.LdapBase.Connect;
public class LdapConnectionPool : ILdapConnectionPool, IConnect
{
private readonly ConcurrentDictionary<int, LdapConnectionWrapper> _pool = new();
private readonly Timer _cleanupTimer;
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
/// <summary>
/// The configuration
/// </summary>
private readonly ILdapConfiguration _configuration;
/// <summary>
/// The logger
/// </summary>
protected readonly ILogger<LdapConnectionPool> _logger;
private readonly object _lock = new object();
public LdapConnectionPool(ILogger<LdapConnectionPool> logger, ILdapConfiguration configuration)
{
this._logger = logger;
this._configuration = configuration;
this._cleanupTimer = new Timer(this.CleanupPool, null, configuration.ConnectionIdleTimeout, configuration.ConnectionIdleTimeout);
}
/// <summary>
/// Connects to Ldap Server.
/// </summary>
/// <returns>LdapConnection.</returns>
public async Task<LdapConnection> ConnectAsync()
{
await this._semaphore.WaitAsync();
{
try
{
var connection = this.GetConnection();
if (connection != null)
{
connection.Bind();
return connection;
}
else
{
this._logger.LogError(
$"with Config: {this._configuration.Server} and Port: {this._configuration.Port}");
throw new Exception("Keine Verbindung zu Ldap-Server möglich");
}
}
catch (Exception e)
{
this._logger.LogError(e.Message, e);
this._logger.LogError(
$"with Config: {this._configuration.Server} and Port: {this._configuration.Port}");
throw;
}
finally
{
this._semaphore.Release();
}
}
}
public LdapConnection GetConnection()
{
// Wenn Verbindungen aus dem Pool abrufen werden, müssen wir verhindern,
// dass die gleiche Verbindung gleichzeitig an mehrere Anforderer ausgegeben wird.
lock (this._lock)
{
var connection = this._pool.FirstOrDefault(p => p.Value.inUse == false);
if (connection.Value != null)
{
connection.Value.inUse = true;
connection.Value.LastAccessed = DateTime.UtcNow;
this._logger.LogDebug($"[{Thread.CurrentThread.ManagedThreadId}]: Take connection from pool with ID [{connection.Value.Connection.GetHashCode()}] ");
return connection.Value.Connection;
}
// Kein Treffer ? Dann neue Verbindung
var newConnection = this.CreateNewConnection().Connection;
this._logger.LogDebug($"[{Thread.CurrentThread.ManagedThreadId}]:Create connection with ID [{newConnection.GetHashCode()}] ");
return newConnection;
}
}
public void ReturnConnection(LdapConnection connection)
{
if (connection == null) throw new ArgumentNullException(nameof(connection));
this._logger.LogDebug($"[{Thread.CurrentThread.ManagedThreadId}]:Enter ReturnConnection with Connection ID [{connection.GetHashCode()}] ");
var key = connection.GetHashCode();
// LdapConnectionWrapper wrapper = new LdapConnectionWrapper(connection);
// bool foundConnection = this._pool.TryGetValue(key, out wrapper);
if (this._pool.TryGetValue(key, out LdapConnectionWrapper wrapper))
{
// Mark the connection as not in use if it's found in the pool
wrapper.inUse = false;
wrapper.LastAccessed = DateTime.UtcNow;
this._logger.LogDebug($"Return existing connection to pool with ID {key}.");
}
else
{
// Only add new connections to the pool if under max size
if (this._pool.Count < this._configuration.MaxPoolSize)
{
var added = this._pool.TryAdd(key, new LdapConnectionWrapper(connection) { inUse = false, LastAccessed = DateTime.UtcNow });
if (added)
{
this._logger.LogDebug($"Added new connection to pool with ID {key}.");
}
}
else
{
// Dispose of the connection if the pool is full
this._logger.LogDebug($"Pool is full. Disposing connection with ID {key}.");
connection.Dispose();
}
}
}
private LdapConnectionWrapper CreateNewConnection()
{
lock (this._lock)
{
this._logger.LogDebug($"[{Thread.CurrentThread.ManagedThreadId}]:Create new connection ... ");
this._logger.LogDebug(
$"[{Thread.CurrentThread.ManagedThreadId}]:Connecting with SecureBasic to: {this._configuration.Server} and Port: {this._configuration.Port}");
var ldapIdentifier = new LdapDirectoryIdentifier(this._configuration.Server, this._configuration.Port);
LdapConnection connection = new LdapConnection(ldapIdentifier)
{
AuthType = AuthType.Basic,
Credential = new NetworkCredential(this._configuration.Username, this._configuration.Password),
SessionOptions =
{
ProtocolVersion = 3,
// Specifies usage of "ldaps://" scheme
SecureSocketLayer = true,
}
};
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// TODO: Remove only for development!
// This option is not working on Linux: https://github.com/dotnet/runtime/issues/60972
connection.SessionOptions.VerifyServerCertificate = (ldapConnection, certificate) => true;
}
return new(connection);
}
}
private void CleanupPool()
{
lock (this._lock)
{
this._logger.LogDebug($"[{Thread.CurrentThread.ManagedThreadId}]:Try to cleanup connection pool ... ");
// Überprüfe und entferne Verbindungen, die länger als _config.ConnectionIdleTimeout ungenutzt sind
var now = DateTime.UtcNow;
foreach (var wrapper in this._pool)
{
this._logger.LogDebug($"[{Thread.CurrentThread.ManagedThreadId}]:Evaluating Connection with ID [{wrapper.Value.Connection.GetHashCode()}] ");
if ((now - wrapper.Value.LastAccessed) > this._configuration.ConnectionIdleTimeout)
{
this._logger.LogDebug($"[{Thread.CurrentThread.ManagedThreadId}]:Cleanup connection with ID [{wrapper.Value.Connection.GetHashCode()}] ");
if (this._pool.TryRemove(wrapper.Value.Connection.GetHashCode(), out var wrapperToRemove))
{
wrapperToRemove.Connection.Dispose();
}
}
}
}
}
public void Dispose()
{
// this._cleanupTimer.Dispose();
foreach (var wrapper in this._pool)
{
this._logger.LogDebug($"[{Thread.CurrentThread.ManagedThreadId}]:Dispose connection with ID [{wrapper.Value.Connection.GetHashCode()}] ");
wrapper.Value.Connection.Dispose();
}
}
}
With the following test:
private async Task CoyoteTest_Pool()
{
// Arrange
var ldapConnectionPool = new LdapConnectionPool(this.CreateLogger<LdapConnectionPool>(), new LdapConfiguration(){});
// Act
var connection1 = ldapConnectionPool.GetConnection();
ldapConnectionPool.ReturnConnection(connection1);
var connection2 = Task.Run(() =>
{
return ldapConnectionPool.GetConnection();
});
var connection3 = Task.Run(() =>
{
return ldapConnectionPool.GetConnection();
});
await Task.WhenAll(connection2, connection3);
// Assert
connection2.Result.Should().NotBeSameAs(connection3.Result);
}
The text was updated successfully, but these errors were encountered:
DanielHabenicht
changed the title
System.NullReferenceException when using Timer
System.NullReferenceException when using Timer in test class
Apr 13, 2024
Hi and sorry for the relatively low effort bug report. (which I think is a bug, in how it is analyzed?)
I think through the way Coyote is executing the test it produces the following error (which never occurs when executing the code normally):
When testing this class
With the following test:
The text was updated successfully, but these errors were encountered: