2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2026-06-13 14:38:43 +00:00

Deadlock test

This commit is contained in:
2026-06-05 17:12:17 +03:00
parent 25d7449d44
commit 8723031e93
2 changed files with 37 additions and 6 deletions
@@ -63,6 +63,8 @@ partial class EpConnection
// (e.g. two concurrent fetches A<->B) so a placeholder can break the deadlock, while // (e.g. two concurrent fetches A<->B) so a placeholder can break the deadlock, while
// independent/app-facing fetches of an in-flight resource simply wait for full attachment. // independent/app-facing fetches of an in-flight resource simply wait for full attachment.
readonly Dictionary<uint, HashSet<uint>> _fetchBlockedOn = new Dictionary<uint, HashSet<uint>>(); readonly Dictionary<uint, HashSet<uint>> _fetchBlockedOn = new Dictionary<uint, HashSet<uint>>();
readonly object _deliveredRootsLock = new object();
readonly Dictionary<uint, WeakReference<EpResource>> _deliveredRoots = new Dictionary<uint, WeakReference<EpResource>>();
/// <summary> /// <summary>
/// Strategy FetchResource uses for an in-flight resource. Defaults to the new wait + cycle /// Strategy FetchResource uses for an in-flight resource. Defaults to the new wait + cycle
@@ -1990,10 +1992,10 @@ partial class EpConnection
req.Then(result => req.Then(result =>
{ {
// The resource is being handed to the application: publish its fully-attached // The resource is being handed to the application: remember it and publish its graph
// graph so that, if any dependency is only partially attached, it stays unpublished. // once all reachable dependencies have attached.
if (result is EpResource resource) if (result is EpResource resource)
PublishGraph(resource); TrackDeliveredRoot(resource);
rt.Trigger(result); rt.Trigger(result);
}).Error(ex => rt.TriggerError(ex)); }).Error(ex => rt.TriggerError(ex));
@@ -2134,7 +2136,7 @@ partial class EpConnection
reachable.Add(node); reachable.Add(node);
if (node.Status != ResourceStatus.Attached) if (node.Status != ResourceStatus.Attached && node.Status != ResourceStatus.Published)
{ {
fullyAttached = false; fullyAttached = false;
continue; // do not traverse into a not-yet-attached node continue; // do not traverse into a not-yet-attached node
@@ -2149,6 +2151,32 @@ partial class EpConnection
node.Publish(); node.Publish();
} }
void TrackDeliveredRoot(EpResource root)
{
lock (_deliveredRootsLock)
_deliveredRoots[root.ResourceInstanceId] = new WeakReference<EpResource>(root);
TryPublishDeliveredRoots();
}
void TryPublishDeliveredRoots()
{
lock (_deliveredRootsLock)
{
var stale = new List<uint>();
foreach (var pair in _deliveredRoots)
{
if (pair.Value.TryGetTarget(out var root))
PublishGraph(root);
else
stale.Add(pair.Key);
}
foreach (var key in stale)
_deliveredRoots.Remove(key);
}
}
public AsyncReply<EpResource> FetchResource(uint id, uint[] requestSequence) public AsyncReply<EpResource> FetchResource(uint id, uint[] requestSequence)
{ {
//lock (fetchLock) //lock (fetchLock)
@@ -2288,6 +2316,7 @@ partial class EpConnection
_attachedResources[id] = new WeakReference<EpResource>(dr); _attachedResources[id] = new WeakReference<EpResource>(dr);
// attached: no longer part of the in-flight wait-for graph. // attached: no longer part of the in-flight wait-for graph.
ClearFetchNode(id); ClearFetchNode(id);
TryPublishDeliveredRoots();
reply.Trigger(dr); reply.Trigger(dr);
}).Error(ex => { _resourceRequests.Remove(id); ClearFetchNode(id); reply.TriggerError(ex); }); }).Error(ex => { _resourceRequests.Remove(id); ClearFetchNode(id); reply.TriggerError(ex); });
}; };
@@ -2418,6 +2447,7 @@ partial class EpConnection
_neededResources.Remove(id); _neededResources.Remove(id);
_attachedResources[id] = new WeakReference<EpResource>(resource); _attachedResources[id] = new WeakReference<EpResource>(resource);
ClearFetchNode(id); ClearFetchNode(id);
TryPublishDeliveredRoots();
reply.Trigger(resource); reply.Trigger(resource);
}) })
.Error(ex => { _resourceRequests.Remove(id); ClearFetchNode(id); reply.TriggerError(ex); }); .Error(ex => { _resourceRequests.Remove(id); ClearFetchNode(id); reply.TriggerError(ex); });
@@ -139,7 +139,7 @@ public class DeadlockIntegrationTests
if (node == null || !seen.Add(node.ResourceInstanceId)) if (node == null || !seen.Add(node.ResourceInstanceId))
continue; continue;
if (node.Status != Resource.ResourceStatus.Attached) if (node.Status != Resource.ResourceStatus.Attached && node.Status != Resource.ResourceStatus.Published)
{ {
allAttached = false; allAttached = false;
continue; // do not traverse into a partially attached node continue; // do not traverse into a partially attached node
@@ -293,7 +293,8 @@ public class DeadlockIntegrationTests
if (node.Status != ResourceStatus.Published) if (node.Status != ResourceStatus.Published)
unpublished++; unpublished++;
if ((node.Status == ResourceStatus.Attached) && node.TryGetPropertyValue((byte)1, out var linksObj) && linksObj is IEnumerable links) if ((node.Status == ResourceStatus.Attached || node.Status == ResourceStatus.Published)
&& node.TryGetPropertyValue((byte)1, out var linksObj) && linksObj is IEnumerable links)
foreach (var child in links) foreach (var child in links)
if (child is EpResource childResource) if (child is EpResource childResource)
queue.Enqueue(childResource); queue.Enqueue(childResource);