using System.Runtime.CompilerServices; namespace SosDemo; internal static class Program { private static readonly object GateA = new(); private static readonly object GateB = new(); private static readonly ManualResetEventSlim Park = new(false); private static int Main(string[] args) { var scenario = args.Length > 0 ? args[0] : "help"; Console.WriteLine($"PID={Environment.ProcessId} scenario={scenario}"); switch (scenario) { case "lock": return LockContention(); case "deadlock": return Deadlock(); case "crash": return Crash(); case "finalizer": return Finalizer(); case "threads": return Threads(); default: Console.WriteLine("usage: SosDemo "); return 1; } } // ---- lock contention: one owner holds the monitor, several workers block on it private static int LockContention() { var owner = new Thread(() => { lock (GateA) { Console.WriteLine("owner acquired GateA, holding"); Thread.Sleep(Timeout.Infinite); } }) { Name = "owner", IsBackground = true }; owner.Start(); Thread.Sleep(200); for (var i = 0; i < 3; i++) { var n = i; new Thread(() => ChargeCustomer(n)) { Name = $"worker-{n}", IsBackground = true }.Start(); } Thread.Sleep(Timeout.Infinite); return 0; } private static void ChargeCustomer(int id) { // blocks here waiting for the contended monitor lock (GateA) { Console.WriteLine($"worker {id} got the lock"); } } // ---- classic two-lock deadlock private static int Deadlock() { var t1 = new Thread(() => { lock (GateA) { Thread.Sleep(300); lock (GateB) { Console.WriteLine("t1 done"); } } }) { Name = "order-processor", IsBackground = true }; var t2 = new Thread(() => { lock (GateB) { Thread.Sleep(300); lock (GateA) { Console.WriteLine("t2 done"); } } }) { Name = "inventory-sync", IsBackground = true }; t1.Start(); t2.Start(); Thread.Sleep(Timeout.Infinite); return 0; } // ---- unhandled nested exception (relies on DOTNET_DbgEnableMiniDump=1) private static int Crash() { var t = new Thread(RunReport) { Name = "report-builder" }; t.Start(); t.Join(); return 0; } private static void RunReport() { try { LoadAccount(42); } catch (Exception ex) { throw new InvalidOperationException("Report generation failed for account 42", ex); } } private static void LoadAccount(int id) => throw new KeyNotFoundException($"Account {id} is not in the cache"); // ---- finalizer queue backlog: finalizer thread is stuck, objects pile up private static int Finalizer() { WedgeFinalizerThread(); GC.Collect(); Thread.Sleep(300); QueueFinalizableGarbage(); GC.Collect(); GC.Collect(2, GCCollectionMode.Forced, blocking: true); Thread.Sleep(500); Console.WriteLine("finalizable objects queued"); Thread.Sleep(Timeout.Infinite); return 0; } [MethodImpl(MethodImplOptions.NoInlining)] private static void WedgeFinalizerThread() => _ = new StuckFinalizer(); [MethodImpl(MethodImplOptions.NoInlining)] private static void QueueFinalizableGarbage() { for (var i = 0; i < 20000; i++) _ = new PendingHandle(); } // ---- several named threads parked in distinct call chains private static int Threads() { new Thread(HttpListenerLoop) { Name = "http-listener", IsBackground = true }.Start(); new Thread(QueueConsumerLoop) { Name = "queue-consumer", IsBackground = true }.Start(); new Thread(CacheEvictionLoop) { Name = "cache-eviction", IsBackground = true }.Start(); Thread.Sleep(Timeout.Infinite); return 0; } private static void HttpListenerLoop() => AcceptConnections(); private static void AcceptConnections() => Park.Wait(); private static void QueueConsumerLoop() => DequeueMessages(); private static void DequeueMessages() => Park.Wait(); private static void CacheEvictionLoop() => SweepExpiredEntries(); private static void SweepExpiredEntries() => Park.Wait(); } internal sealed class StuckFinalizer { ~StuckFinalizer() { // wedges the finalizer thread so the queue cannot drain Thread.Sleep(Timeout.Infinite); } } internal sealed class PendingHandle { private readonly byte[] _payload = new byte[256]; ~PendingHandle() => GC.KeepAlive(_payload); }