Skip to content

Commit 51a363e

Browse files
authored
Fix race on stopping monitor (#1377)
* Fix race on stopping monitor This PR fixes the race condition when disposing of the RegistryMonitor and when detecting the stop. This PR modifies Dispose to call Stop() which will cause the wait handler to fire and stop the Monitor thread which will properly dispose of the object.
1 parent cf94006 commit 51a363e

1 file changed

Lines changed: 41 additions & 37 deletions

File tree

src/DebugEngineHost/RegistryMonitor.cs

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,15 @@ private static extern int RegNotifyChangeKeyValue(SafeRegistryHandle hKey, bool
4747

4848
#endregion
4949

50-
private HostConfigurationSection _section;
50+
private readonly HostConfigurationSection _section;
5151
private readonly bool _watchSubtree;
5252

53-
// Set when registry value is changed
54-
private AutoResetEvent m_changeEvent;
55-
5653
// Set when monitoring is stopped
57-
private AutoResetEvent m_stoppedEvent;
54+
private AutoResetEvent _stoppedEvent;
55+
56+
// Members to handle multiple stop calls.
57+
private bool _isStopped = false;
58+
private readonly object _stopLock = new object();
5859

5960
/// <summary>
6061
/// Occurs when the specified registry key has changed.
@@ -80,9 +81,13 @@ public void Start()
8081

8182
public void Stop()
8283
{
83-
if (m_stoppedEvent != null)
84+
lock (_stopLock)
8485
{
85-
m_stoppedEvent.Set();
86+
if (!_isStopped)
87+
{
88+
_stoppedEvent?.Set();
89+
_isStopped = true;
90+
}
8691
}
8792
}
8893

@@ -93,53 +98,52 @@ private void Monitor()
9398
bool stopped = false;
9499
try
95100
{
96-
m_stoppedEvent = new AutoResetEvent(false);
97-
m_changeEvent = new AutoResetEvent(false);
98-
99-
IntPtr handle = m_changeEvent.SafeWaitHandle.DangerousGetHandle();
100-
101-
int errorCode = RegNotifyChangeKeyValue(_section.Handle, _watchSubtree, RegChangeNotifyFilter.REG_NOTIFY_CHANGE_NAME | RegChangeNotifyFilter.REG_NOTIFY_CHANGE_LAST_SET, handle, true);
102-
if (errorCode != 0) // 0 is ERROR_SUCCESS
103-
{
104-
_nativsLogger?.WriteLine(LogLevel.Error, Resource.Error_WatchRegistry, errorCode);
105-
}
106-
else
101+
_stoppedEvent = new AutoResetEvent(false);
102+
using (AutoResetEvent registryChangedEvent = new AutoResetEvent(false))
107103
{
108-
while (!stopped)
109-
{
110-
int waitResult = WaitHandle.WaitAny(new WaitHandle[] { m_stoppedEvent, m_changeEvent });
104+
IntPtr handle = registryChangedEvent.SafeWaitHandle.DangerousGetHandle();
111105

112-
if (waitResult == 0)
113-
{
114-
stopped = true;
115-
}
116-
else
106+
int errorCode = RegNotifyChangeKeyValue(_section.Handle, _watchSubtree, RegChangeNotifyFilter.REG_NOTIFY_CHANGE_NAME | RegChangeNotifyFilter.REG_NOTIFY_CHANGE_LAST_SET, handle, true);
107+
if (errorCode != 0) // 0 is ERROR_SUCCESS
108+
{
109+
_nativsLogger?.WriteLine(LogLevel.Error, Resource.Error_WatchRegistry, errorCode);
110+
}
111+
else
112+
{
113+
while (!stopped)
117114
{
118-
errorCode = RegNotifyChangeKeyValue(_section.Handle, _watchSubtree, RegChangeNotifyFilter.REG_NOTIFY_CHANGE_NAME | RegChangeNotifyFilter.REG_NOTIFY_CHANGE_LAST_SET, handle, true);
119-
if (errorCode != 0) // 0 is ERROR_SUCCESS
115+
int waitResult = WaitHandle.WaitAny(new WaitHandle[] { _stoppedEvent, registryChangedEvent });
116+
117+
if (waitResult == 0)
120118
{
121-
_nativsLogger?.WriteLine(LogLevel.Error, Resource.Error_WatchRegistry, errorCode);
122-
break;
119+
stopped = true;
120+
}
121+
else
122+
{
123+
errorCode = RegNotifyChangeKeyValue(_section.Handle, _watchSubtree, RegChangeNotifyFilter.REG_NOTIFY_CHANGE_NAME | RegChangeNotifyFilter.REG_NOTIFY_CHANGE_LAST_SET, handle, true);
124+
if (errorCode != 0) // 0 is ERROR_SUCCESS
125+
{
126+
_nativsLogger?.WriteLine(LogLevel.Error, Resource.Error_WatchRegistry, errorCode);
127+
break;
128+
}
129+
RegChanged?.Invoke(this, null);
123130
}
124-
RegChanged?.Invoke(this, null);
125131
}
126132
}
127133
}
128134
}
129135
finally
130136
{
131-
_section.Dispose();
132-
m_stoppedEvent?.Dispose();
133-
m_changeEvent?.Dispose();
137+
_stoppedEvent.Dispose();
138+
_stoppedEvent = null;
134139

135-
m_stoppedEvent = null;
136-
m_changeEvent = null;
140+
_section.Dispose();
137141
}
138142
}
139143

140144
public void Dispose()
141145
{
142-
m_stoppedEvent?.Dispose();
146+
Stop(); // Stopping the monitor will dispose of the AutoResetEvent and HostConfigurationSection
143147
}
144148
}
145149
}

0 commit comments

Comments
 (0)