Skip to content

Commit f3fbeca

Browse files
committed
Merge pull request #181 from jazzdelightsme/master
Allow for clean exit on AppDomain unload.
2 parents ce56092 + 8487cf2 commit f3fbeca

File tree

1 file changed

+27
-9
lines changed

1 file changed

+27
-9
lines changed

PSReadLine/ReadLine.cs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ public partial class PSConsoleReadLine : IPSConsoleReadLineMockableMethods
2828
private Thread _readKeyThread;
2929
private AutoResetEvent _readKeyWaitHandle;
3030
private AutoResetEvent _keyReadWaitHandle;
31-
private AutoResetEvent _closingWaitHandle;
32-
private WaitHandle[] _waitHandles;
31+
private ManualResetEvent _closingWaitHandle;
32+
private WaitHandle[] _threadProcWaitHandles;
33+
private WaitHandle[] _requestKeyWaitHandles;
3334
private bool _captureKeys;
3435
private readonly Queue<ConsoleKeyInfo> _savedKeys;
3536
private uint _prePSReadlineConsoleMode;
@@ -73,8 +74,10 @@ private void ReadKeyThreadProc()
7374
var stopwatch = new Stopwatch();
7475
while (true)
7576
{
76-
// Wait until ReadKey tells us to read a key.
77-
_readKeyWaitHandle.WaitOne();
77+
// Wait until ReadKey tells us to read a key (or it's time to exit).
78+
int handleId = WaitHandle.WaitAny(_singleton._threadProcWaitHandles);
79+
if (handleId == 1) // It was the _closingWaitHandle that was signaled.
80+
break;
7881

7982
stopwatch.Restart();
8083
while (_mockableMethods.KeyAvailable())
@@ -127,7 +130,7 @@ private static ConsoleKeyInfo ReadKey()
127130
// - the console is exiting
128131
// - 300ms - to process events if we're idle
129132

130-
handleId = WaitHandle.WaitAny(_singleton._waitHandles, 300);
133+
handleId = WaitHandle.WaitAny(_singleton._requestKeyWaitHandles, 300);
131134
if (handleId != WaitHandle.WaitTimeout)
132135
break;
133136

@@ -451,15 +454,30 @@ static PSConsoleReadLine()
451454

452455
_breakHandlerGcHandle = GCHandle.Alloc(new BreakHandler(_singleton.BreakHandler));
453456
NativeMethods.SetConsoleCtrlHandler((BreakHandler) _breakHandlerGcHandle.Target, true);
454-
_singleton._readKeyThread = new Thread(_singleton.ReadKeyThreadProc) {IsBackground = true};
455-
_singleton._readKeyThread.Start();
456457
_singleton._readKeyWaitHandle = new AutoResetEvent(false);
457458
_singleton._keyReadWaitHandle = new AutoResetEvent(false);
458-
_singleton._closingWaitHandle = new AutoResetEvent(false);
459-
_singleton._waitHandles = new WaitHandle[] { _singleton._keyReadWaitHandle, _singleton._closingWaitHandle };
459+
_singleton._closingWaitHandle = new ManualResetEvent(false);
460+
_singleton._requestKeyWaitHandles = new WaitHandle[] { _singleton._keyReadWaitHandle, _singleton._closingWaitHandle };
461+
_singleton._threadProcWaitHandles = new WaitHandle[] { _singleton._readKeyWaitHandle, _singleton._closingWaitHandle };
460462

461463
// This is only used for post-mortem debugging - 200 keys should be enough to reconstruct most command lines.
462464
_lastNKeys = new HistoryQueue<ConsoleKeyInfo>(200);
465+
466+
// This is for a "being hosted in an alternate appdomain scenario" (the
467+
// DomainUnload event is not raised for the default appdomain). It allows us
468+
// to exit cleanly when the appdomain is unloaded but the process is not going
469+
// away.
470+
if (!AppDomain.CurrentDomain.IsDefaultAppDomain())
471+
{
472+
AppDomain.CurrentDomain.DomainUnload += (x, y) =>
473+
{
474+
_singleton._closingWaitHandle.Set();
475+
_singleton._readKeyThread.Join(); // may need to wait for history to be written
476+
};
477+
}
478+
479+
_singleton._readKeyThread = new Thread(_singleton.ReadKeyThreadProc) {IsBackground = true};
480+
_singleton._readKeyThread.Start();
463481
}
464482

465483
private PSConsoleReadLine()

0 commit comments

Comments
 (0)