Skip to content

Commit ed924e2

Browse files
committed
Fix deadlock: ensure workers always remove from runningThreads on cancellation
When AbortBackgroundRecalc() is called before DoBackgroundRecalcDisplayList creates recalcDone, the abort cannot signal it. If workers subsequently see the cancellation at the early-exit checks and return without removing themselves from runningThreads, recalcDone.Count never reaches zero and recalcDone.Set() is never called, causing WaitOne() to block forever and t.Join() to hang indefinitely. Move the lock(runningThreads) cleanup block outside the cancellation guard so every queued worker unconditionally removes itself from runningThreads and signals recalcDone when it is the last one — regardless of whether it performed any actual work. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014bFdnkBE83221SmUkyXHze
1 parent f7aae9c commit ed924e2

1 file changed

Lines changed: 16 additions & 12 deletions

File tree

CADability/Model.cs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -150,21 +150,25 @@ private void RecalcGeoObject(object state)
150150
{
151151
// hier wird die eigentliche Arbeit gemacht
152152
go.PrepareDisplayList(data.Precision);
153-
lock (runningThreads)
154-
{
155-
runningThreads.Remove(data.RecalcID); // wirft keine Exception wenn nicht drin
156-
// recalcDone hat einen Wert, wenn DoBackgroundRecalcDisplayList alle Objekte in die
157-
// Queue gesteckt hat und auf das Ende wartet
158-
if (recalcDone != null && runningThreads.Count == 0)
159-
{ // wenn der letzte thread fertig ist, nachdem alle in der Liste stehen
160-
// wird recalcDone "signalisiert", d.h. alle Objekte sind jetzt berechnet
161-
// und eine neue Displayliste kann schnell hergestellt werden
162-
recalcDone.Set();
163-
}
164-
}
165153
}
166154
}
167155
}
156+
// Always remove from runningThreads regardless of cancellation. If a worker exits
157+
// early due to cancellation without removing itself, DoBackgroundRecalcDisplayList
158+
// can block forever on recalcDone.WaitOne() when AbortBackgroundRecalc ran before
159+
// recalcDone was created and therefore could not signal it.
160+
lock (runningThreads)
161+
{
162+
runningThreads.Remove(data.RecalcID); // wirft keine Exception wenn nicht drin
163+
// recalcDone hat einen Wert, wenn DoBackgroundRecalcDisplayList alle Objekte in die
164+
// Queue gesteckt hat und auf das Ende wartet
165+
if (recalcDone != null && runningThreads.Count == 0)
166+
{ // wenn der letzte thread fertig ist, nachdem alle in der Liste stehen
167+
// wird recalcDone "signalisiert", d.h. alle Objekte sind jetzt berechnet
168+
// und eine neue Displayliste kann schnell hergestellt werden
169+
recalcDone.Set();
170+
}
171+
}
168172
}
169173

170174
private class RecalcGeoObjectData

0 commit comments

Comments
 (0)