Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions csharp/Platform.Data.Doublets.Tests/DisposalErrorMessagesTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using System.IO;
using Xunit;
using Platform.Data.Doublets.Memory.United.Generic;
using Platform.Memory;

namespace Platform.Data.Doublets.Tests
{
/// <summary>
/// Tests to verify that better error messages are shown when database access is disposed
/// right after the access is created (GitHub issue #174)
/// </summary>
public static class DisposalErrorMessagesTests
{
[Fact]
public static void DisposedLinksThrowObjectDisposedExceptionWithMeaningfulMessage()
{
var links = new UnitedMemoryLinks<uint>(new HeapResizableDirectMemory());

// Create a link and then dispose
var link = links.Create();
links.Dispose();

// Test Count method
var ex = Assert.Throws<ObjectDisposedException>(() => links.Count());
Assert.Contains("Cannot access a disposed Links instance", ex.Message);
Assert.Contains("database connection has been closed", ex.Message);

// Test Create method
ex = Assert.Throws<ObjectDisposedException>(() => links.Create());
Assert.Contains("Cannot access a disposed Links instance", ex.Message);

// Test Delete method
ex = Assert.Throws<ObjectDisposedException>(() => links.Delete(link));
Assert.Contains("Cannot access a disposed Links instance", ex.Message);

// Test Each method
ex = Assert.Throws<ObjectDisposedException>(() => links.Each(handler: l => links.Constants.Continue));
Assert.Contains("Cannot access a disposed Links instance", ex.Message);

// Test Update method
ex = Assert.Throws<ObjectDisposedException>(() => links.Update(new[] { link }, new[] { link }));
Assert.Contains("Cannot access a disposed Links instance", ex.Message);
}

[Fact]
public static void DisposedLinksObjectNameIsCorrectInException()
{
var links = new UnitedMemoryLinks<uint>(new HeapResizableDirectMemory());
links.Dispose();

var ex = Assert.Throws<ObjectDisposedException>(() => links.Count());
Assert.Contains("UnitedMemoryLinks", ex.ObjectName);
}

[Fact]
public static void NonDisposedLinksWorkNormally()
{
using var links = new UnitedMemoryLinks<uint>(new HeapResizableDirectMemory());

// These should all work normally without throwing ObjectDisposedException
var count = links.Count();
var link = links.Create();
links.Each(handler: l => links.Constants.Continue);
// Test update - create another link to update the first one
var link2 = links.Create();
links.Update(new[] { link }, new[] { link, link2, link2 });
links.Delete(link);
links.Delete(link2);
}
}
}
25 changes: 25 additions & 0 deletions csharp/Platform.Data.Doublets/Ffi/Links.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Platform.Converters;
using Platform.Delegates;
Expand Down Expand Up @@ -181,6 +182,25 @@ public class Links<TLinkAddress> : DisposableBase, ILinks<TLinkAddress> where T

private readonly unsafe void* _ptr;

/// <summary>
/// <para>
/// Ensures that this instance has not been disposed.
/// </para>
/// <para></para>
/// </summary>
/// <exception cref="ObjectDisposedException">
/// <para>Thrown when this instance has been disposed.</para>
/// <para></para>
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual void EnsureNotDisposed()
{
if (IsDisposed)
{
throw new ObjectDisposedException(GetType().Name, "Cannot access a disposed Links instance. The database connection has been closed and is no longer available for operations.");
}
}

public Links(string path)
{
TLinkAddress t = default;
Expand All @@ -202,6 +222,7 @@ public Links(string path)

public TLinkAddress Count(IList<TLinkAddress>? restriction)
{
EnsureNotDisposed();
var restrictionLength = restriction?.Count ?? 0;
unsafe
{
Expand Down Expand Up @@ -260,6 +281,7 @@ public TLinkAddress Count(IList<TLinkAddress>? restriction)

public TLinkAddress Each(IList<TLinkAddress>? restriction, ReadHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
var restrictionLength = restriction?.Count ?? 0;
unsafe
{
Expand Down Expand Up @@ -322,6 +344,7 @@ public TLinkAddress Each(IList<TLinkAddress>? restriction, ReadHandler<TLinkAddr

public TLinkAddress Create(IList<TLinkAddress>? substitution, WriteHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
var substitutionLength = substitution?.Count ?? 0;
unsafe
{
Expand Down Expand Up @@ -384,6 +407,7 @@ public TLinkAddress Create(IList<TLinkAddress>? substitution, WriteHandler<TLink

public TLinkAddress Update(IList<TLinkAddress>? restriction, IList<TLinkAddress>? substitution, WriteHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
var restrictionLength = restriction?.Count ?? 0;
var substitutionLength = substitution?.Count ?? 0;
unsafe
Expand Down Expand Up @@ -469,6 +493,7 @@ public TLinkAddress Update(IList<TLinkAddress>? restriction, IList<TLinkAddress>

public TLinkAddress Delete(IList<TLinkAddress>? restriction, WriteHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
var restrictionLength = restriction?.Count ?? 0;
unsafe
{
Expand Down
25 changes: 25 additions & 0 deletions csharp/Platform.Data.Doublets/Ffi/UInt32Links.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Platform.Converters;
using Platform.Delegates;
Expand All @@ -17,6 +18,25 @@ public class UInt32Links : DisposableBase, ILinks<TLinkAddress>

private readonly unsafe void* _ptr;

/// <summary>
/// <para>
/// Ensures that this instance has not been disposed.
/// </para>
/// <para></para>
/// </summary>
/// <exception cref="ObjectDisposedException">
/// <para>Thrown when this instance has been disposed.</para>
/// <para></para>
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual void EnsureNotDisposed()
{
if (IsDisposed)
{
throw new ObjectDisposedException(GetType().Name, "Cannot access a disposed Links instance. The database connection has been closed and is no longer available for operations.");
}
}

public UInt32Links(string path)
{
unsafe
Expand All @@ -30,6 +50,7 @@ public UInt32Links(string path)

public TLinkAddress Count(IList<TLinkAddress>? restriction)
{
EnsureNotDisposed();
unsafe
{
var array = stackalloc uint[restriction.Count];
Expand All @@ -43,6 +64,7 @@ public TLinkAddress Count(IList<TLinkAddress>? restriction)

public TLinkAddress Each(IList<TLinkAddress>? restriction, ReadHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
unsafe
{
Methods.EachCallback_UInt32 callback = (link) => handler != null ? handler(new Link<TLinkAddress>(link.Index, link.Source, link.Target)) : Constants.Continue;
Expand All @@ -57,6 +79,7 @@ public TLinkAddress Each(IList<TLinkAddress>? restriction, ReadHandler<TLinkAddr

public TLinkAddress Create(IList<TLinkAddress>? substitution, WriteHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
unsafe
{
Methods.CreateCallback_UInt32 callback = (before, after) => handler != null ? handler(new Link<TLinkAddress>(before.Index, before.Source, before.Target), new Link<TLinkAddress>(after.Index, after.Source, after.Target)) : Constants.Continue;
Expand All @@ -69,6 +92,7 @@ public TLinkAddress Create(IList<TLinkAddress>? substitution, WriteHandler<TLink

public TLinkAddress Update(IList<TLinkAddress>? restriction, IList<TLinkAddress>? substitution, WriteHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
unsafe
{
var restrictionArray = stackalloc uint[restriction.Count];
Expand All @@ -88,6 +112,7 @@ public TLinkAddress Update(IList<TLinkAddress>? restriction, IList<TLinkAddress>

public TLinkAddress Delete(IList<TLinkAddress>? restriction, WriteHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
unsafe
{
var restrictionArray = stackalloc uint[restriction.Count];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,25 @@ public virtual LinksConstants<TLinkAddress> Constants
get;
}

/// <summary>
/// <para>
/// Ensures that this instance has not been disposed.
/// </para>
/// <para></para>
/// </summary>
/// <exception cref="ObjectDisposedException">
/// <para>Thrown when this instance has been disposed.</para>
/// <para></para>
/// </exception>
[MethodImpl(methodImplOptions: MethodImplOptions.AggressiveInlining)]
protected virtual void EnsureNotDisposed()
{
if (IsDisposed)
{
throw new ObjectDisposedException(GetType().Name, "Cannot access a disposed Links instance. The database connection has been closed and is no longer available for operations.");
}
}

/// <summary>
/// <para>
/// Counts the substitution.
Expand All @@ -242,6 +261,7 @@ public virtual LinksConstants<TLinkAddress> Constants
[MethodImpl(methodImplOptions: MethodImplOptions.AggressiveInlining)]
public virtual TLinkAddress Count(IList<TLinkAddress>? restriction)
{
EnsureNotDisposed();
// Если нет ограничений, тогда возвращаем общее число связей находящихся в хранилище.
if (restriction.Count == 0)
{
Expand Down Expand Up @@ -433,6 +453,7 @@ public virtual TLinkAddress Count(IList<TLinkAddress>? restriction)
[MethodImpl(methodImplOptions: MethodImplOptions.AggressiveInlining)]
public virtual TLinkAddress Each(IList<TLinkAddress>? restriction, ReadHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
var constants = Constants;
var @break = constants.Break;
if (restriction.Count == 0)
Expand Down Expand Up @@ -612,6 +633,7 @@ public virtual TLinkAddress Each(IList<TLinkAddress>? restriction, ReadHandler<T
[MethodImpl(methodImplOptions: MethodImplOptions.AggressiveInlining)]
public virtual TLinkAddress Update(IList<TLinkAddress>? restriction, IList<TLinkAddress>? substitution, WriteHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
var constants = Constants;
var @null = constants.Null;
var externalReferencesRange = constants.ExternalReferencesRange;
Expand Down Expand Up @@ -693,6 +715,7 @@ public virtual TLinkAddress Update(IList<TLinkAddress>? restriction, IList<TLink
[MethodImpl(methodImplOptions: MethodImplOptions.AggressiveInlining)]
public virtual TLinkAddress Create(IList<TLinkAddress>? substitution, WriteHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
ref var header = ref GetHeaderReference();
var freeLink = header.FirstFreeLink;
if ((freeLink != Constants.Null))
Expand Down Expand Up @@ -734,6 +757,7 @@ public virtual TLinkAddress Create(IList<TLinkAddress>? substitution, WriteHandl
[MethodImpl(methodImplOptions: MethodImplOptions.AggressiveInlining)]
public virtual TLinkAddress Delete(IList<TLinkAddress>? restriction, WriteHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
ref var header = ref GetHeaderReference();
var link = restriction[index: Constants.IndexPart];
var before = GetLinkStruct(linkIndex: link);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,25 @@ public virtual LinksConstants<TLinkAddress> Constants
get;
}

/// <summary>
/// <para>
/// Ensures that this instance has not been disposed.
/// </para>
/// <para></para>
/// </summary>
/// <exception cref="ObjectDisposedException">
/// <para>Thrown when this instance has been disposed.</para>
/// <para></para>
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual void EnsureNotDisposed()
{
if (IsDisposed)
{
throw new ObjectDisposedException(GetType().Name, "Cannot access a disposed Links instance. The database connection has been closed and is no longer available for operations.");
}
}

/// <summary>
/// <para>
/// Initializes a new <see cref="UnitedMemoryLinksBase"/> instance.
Expand Down Expand Up @@ -207,6 +226,7 @@ protected virtual void Init(IResizableDirectMemory memory, long memoryReservatio
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual TLinkAddress Count(IList<TLinkAddress>? restriction)
{
EnsureNotDisposed();
// Если нет ограничений, тогда возвращаем общее число связей находящихся в хранилище.
if (restriction.Count == 0)
{
Expand Down Expand Up @@ -340,6 +360,7 @@ public virtual TLinkAddress Count(IList<TLinkAddress>? restriction)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual TLinkAddress Each(IList<TLinkAddress>? restriction, ReadHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
var constants = Constants;
var @break = constants.Break;
if (restriction.Count == 0)
Expand Down Expand Up @@ -472,6 +493,7 @@ public virtual TLinkAddress Each(IList<TLinkAddress>? restriction, ReadHandler<T
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual TLinkAddress Update(IList<TLinkAddress>? restriction, IList<TLinkAddress>? substitution, WriteHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
var constants = Constants;
var @null = constants.Null;
var linkIndex = this.GetIndex(restriction);
Expand Down Expand Up @@ -508,6 +530,7 @@ public virtual TLinkAddress Update(IList<TLinkAddress>? restriction, IList<TLink
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual TLinkAddress Create(IList<TLinkAddress>? substitution, WriteHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
ref var header = ref GetHeaderReference();
var freeLink = header.FirstFreeLink;
if (!AreEqual(freeLink, Constants.Null))
Expand Down Expand Up @@ -547,6 +570,7 @@ public virtual TLinkAddress Create(IList<TLinkAddress>? substitution, WriteHandl
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual TLinkAddress Delete(IList<TLinkAddress>? restriction, WriteHandler<TLinkAddress>? handler)
{
EnsureNotDisposed();
ref var header = ref GetHeaderReference();
var link = restriction[Constants.IndexPart];
var before = GetLinkStruct(link);
Expand Down
Loading
Loading