Added keep alive setting (#41109)
* Added keep alive setting * Updated to use DistroTimeout instead
Craig Loewen committed
Aug 6, 2026 at 20:23 UTC
2c93143efca8b3fd3b4c248b57cfcbe1eba488e0
9 files changed
+179
-2
localization/strings/en-US/Resources.resw
+27
@@ -1985,6 +1985,18 @@ You can also access more VS Code Remote options through the command palette with
1985
<data name="Settings_VMIdleTimeout.Description" xml:space="preserve">
1986
<value>The number of milliseconds that a VM is idle, before it is shut down.</value>
1987
</data>
1988
+ <data name="Settings_KeepWslRunning.Header" xml:space="preserve">
1989
+ <value>Keep WSL running</value>
1990
+ </data>
1991
+ <data name="Settings_KeepWslRunning.Description" xml:space="preserve">
1992
+ <value>Keep running distributions, and the WSL virtual machine hosting them, from being automatically shut down after they have been idle.</value>
1993
+ </data>
1994
+ <data name="Settings_KeepWslRunningToggleSwitch.AutomationProperties.Name" xml:space="preserve">
1995
+ <value>Keep WSL running.</value>
1996
+ </data>
1997
+ <data name="Settings_KeepWslRunningToggleSwitch.AutomationProperties.HelpText" xml:space="preserve">
1998
+ <value>Keep running distributions, and the WSL virtual machine hosting them, from being automatically shut down after they have been idle.</value>
1999
+ </data>
2000
<data name="Settings_VMIdleTimeoutResetButton.Content" xml:space="preserve">
2001
<value>Reset timeout</value>
2002
</data>
@@ -1994,6 +2006,21 @@ You can also access more VS Code Remote options through the command palette with
2006
<data name="Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText" xml:space="preserve">
2007
<value>The number of milliseconds that a VM is idle, before it is shut down.</value>
2008
</data>
2009
+ <data name="Settings_InstanceIdleTimeout.Header" xml:space="preserve">
2010
+ <value>Distribution idle timeout</value>
2011
+ </data>
2012
+ <data name="Settings_InstanceIdleTimeout.Description" xml:space="preserve">
2013
+ <value>The number of milliseconds that a distribution is idle, before it is terminated.</value>
2014
+ </data>
2015
+ <data name="Settings_InstanceIdleTimeoutResetButton.Content" xml:space="preserve">
2016
+ <value>Reset timeout</value>
2017
+ </data>
2018
+ <data name="Settings_InstanceIdleTimeoutTextBox.AutomationProperties.Name" xml:space="preserve">
2019
+ <value>Distribution idle timeout</value>
2020
+ </data>
2021
+ <data name="Settings_InstanceIdleTimeoutTextBox.AutomationProperties.HelpText" xml:space="preserve">
2022
+ <value>The number of milliseconds that a distribution is idle, before it is terminated.</value>
2023
+ </data>
2024
<data name="Settings_ApplyChangesButton.Content" xml:space="preserve">
2025
<value>Apply changes</value>
2026
</data>
src/windows/inc/WslCoreConfigInterface.h
+1
@@ -47,6 +47,7 @@ enum WslConfigEntry
47
KernelPath,
48
SystemDistroPath,
49
KernelModulesPath,
50
+ InstanceIdleTimeout,
51
};
52
53
enum NetworkingConfiguration
src/windows/libwsl/WslCoreConfigInterface.cpp
+11
@@ -195,6 +195,10 @@ WslConfigSetting GetWslConfigSetting(WslConfig_t wslConfig, WslConfigEntry wslCo
195
static_assert(std::is_same<decltype(wslConfigSetting.StringValue), decltype(wslConfig->Config.KernelModulesPath.c_str())>::value);
196
wslConfigSetting.StringValue = wslConfig->Config.KernelModulesPath.c_str();
197
break;
198
+ case InstanceIdleTimeout:
199
+ static_assert(std::is_same<decltype(wslConfigSetting.Int32Value), decltype(wslConfig->Config.InstanceIdleTimeout)>::value);
200
+ wslConfigSetting.Int32Value = wslConfig->Config.InstanceIdleTimeout;
201
+ break;
202
default:
203
FAIL_FAST();
204
}
@@ -560,6 +564,13 @@ unsigned long SetWslConfigSetting(WslConfig_t wslConfig, WslConfigSetting wslCon
564
wslConfigSetting.StringValue,
565
wslConfig->Config.KernelModulesPath);
566
}
567
+ case InstanceIdleTimeout:
568
+ return SetWslConfigSetting(
569
+ wslConfig,
570
+ ConfigSetting::InstanceIdleTimeout,
571
+ defaultConfig.InstanceIdleTimeout,
572
+ wslConfigSetting.Int32Value,
573
+ wslConfig->Config.InstanceIdleTimeout);
574
default:
575
FAIL_FAST();
576
}
src/windows/wslsettings/Contracts/Services/IWslConfigService.cs
+1
@@ -76,6 +76,7 @@ public static class WslConfigEntryExtensions
76
77
WslConfigEntry.ProcessorCount or
78
WslConfigEntry.InitialAutoProxyTimeout or
79
+ WslConfigEntry.InstanceIdleTimeout or
80
WslConfigEntry.VMIdleTimeout => WslConfigValueKind.Int32,
81
82
WslConfigEntry.MemorySizeBytes or
src/windows/wslsettings/LibWsl.cs
+2
-1
@@ -43,7 +43,8 @@ namespace LibWsl
43
HardwarePerformanceCountersEnabled = 23,
44
KernelPath = 24,
45
SystemDistroPath = 25,
46
- KernelModulesPath = 26
46
+ KernelModulesPath = 26,
47
+ InstanceIdleTimeout = 27
48
}
49
50
public enum NetworkingConfiguration
src/windows/wslsettings/ViewModels/Settings/OptionalFeaturesViewModel.cs
+106
@@ -16,12 +16,16 @@ public partial class OptionalFeaturesViewModel : WslConfigSettingViewModel
16
private IWslConfigSetting? _sparseVHD;
17
private IWslConfigSetting? _vMIdleTimeout;
18
private int _defaultVMIdleTimeout;
19
+ private IWslConfigSetting? _instanceIdleTimeout;
20
+ private int _defaultInstanceIdleTimeout;
21
+ private int _previousInstanceIdleTimeout;
22
23
public OptionalFeaturesViewModel()
24
{
25
InitializeConfigSettings();
26
27
VMIdleTimeout_ResetEnabled = !Equals(_defaultVMIdleTimeout, _vMIdleTimeout!.Int32Value);
28
+ InstanceIdleTimeout_ResetEnabled = !Equals(_defaultInstanceIdleTimeout, _instanceIdleTimeout!.Int32Value);
29
}
30
31
protected override void InitializeConfigSettings()
@@ -33,8 +37,13 @@ public partial class OptionalFeaturesViewModel : WslConfigSettingViewModel
37
_safeMode = wslConfigService.GetWslConfigSetting(WslConfigEntry.SafeModeEnabled);
38
_sparseVHD = wslConfigService.GetWslConfigSetting(WslConfigEntry.SparseVHDEnabled);
39
_vMIdleTimeout = wslConfigService.GetWslConfigSetting(WslConfigEntry.VMIdleTimeout);
40
+ _instanceIdleTimeout = wslConfigService.GetWslConfigSetting(WslConfigEntry.InstanceIdleTimeout);
41
42
_defaultVMIdleTimeout = wslConfigService.GetWslConfigSetting(WslConfigEntry.VMIdleTimeout, true).Int32Value;
43
+ _defaultInstanceIdleTimeout = wslConfigService.GetWslConfigSetting(WslConfigEntry.InstanceIdleTimeout, true).Int32Value;
44
+
45
+ var configuredInstanceIdleTimeout = _instanceIdleTimeout!.Int32Value;
46
+ _previousInstanceIdleTimeout = configuredInstanceIdleTimeout < 0 ? _defaultInstanceIdleTimeout : configuredInstanceIdleTimeout;
47
}
48
49
public List<string> MemoryReclaimModes
@@ -72,6 +81,46 @@ public partial class OptionalFeaturesViewModel : WslConfigSettingViewModel
81
set { Set(ref _sparseVHD!, value); }
82
}
83
84
+ // Keeping WSL running is driven by the distribution idle timeout: a negative value tells the
85
+ // service to never idle-terminate a distribution, and because the VM is only considered idle
86
+ // once every distribution has stopped, this keeps the WSL VM running as well.
87
+ public bool IsOnKeepWslRunning
88
+ {
89
+ get { return _instanceIdleTimeout!.Int32Value < 0; }
90
+ set
91
+ {
92
+ if (value)
93
+ {
94
+ var currentInstanceIdleTimeout = _instanceIdleTimeout!.Int32Value;
95
+ if (currentInstanceIdleTimeout >= 0)
96
+ {
97
+ _previousInstanceIdleTimeout = currentInstanceIdleTimeout;
98
+ }
99
+
100
+ Set(ref _instanceIdleTimeout!, -1, nameof(IsOnKeepWslRunning));
101
+ }
102
+ else
103
+ {
104
+ Set(ref _instanceIdleTimeout!, _previousInstanceIdleTimeout, nameof(IsOnKeepWslRunning));
105
+ }
106
+
107
+ OnPropertyChanged(nameof(InstanceIdleTimeout));
108
+ OnPropertyChanged(nameof(InstanceIdleTimeoutEnabled));
109
+ OnPropertyChanged(nameof(VMIdleTimeoutEnabled));
110
+ InstanceIdleTimeout_ResetEnabled = !Equals(_defaultInstanceIdleTimeout, _instanceIdleTimeout!.Int32Value);
111
+ }
112
+ }
113
+
114
+ public bool VMIdleTimeoutEnabled
115
+ {
116
+ get { return !IsOnKeepWslRunning; }
117
+ }
118
+
119
+ public bool InstanceIdleTimeoutEnabled
120
+ {
121
+ get { return !IsOnKeepWslRunning; }
122
+ }
123
+
124
public string VMIdleTimeout
125
{
126
get
@@ -120,4 +169,61 @@ public partial class OptionalFeaturesViewModel : WslConfigSettingViewModel
169
}
170
171
public ICommand VMIdleTimeout_ResetCommand => new RelayCommand<string>(VMIdleTimeout_ResetExecuted);
172
+
173
+ public string InstanceIdleTimeout
174
+ {
175
+ get
176
+ {
177
+ return _instanceIdleTimeout!.Int32Value.ToString();
178
+ }
179
+ set
180
+ {
181
+ if (ValidateInput(value, Constants.IntegerRegex))
182
+ {
183
+ if (Int32.TryParse(value, out int parsedValue))
184
+ {
185
+ Set(ref _instanceIdleTimeout!, parsedValue);
186
+ if (parsedValue >= 0)
187
+ {
188
+ _previousInstanceIdleTimeout = parsedValue;
189
+ }
190
+
191
+ OnPropertyChanged(nameof(IsOnKeepWslRunning));
192
+ OnPropertyChanged(nameof(InstanceIdleTimeoutEnabled));
193
+ OnPropertyChanged(nameof(VMIdleTimeoutEnabled));
194
+ }
195
+ else
196
+ {
197
+ OnPropertyChanged();
198
+ }
199
+ }
200
+ }
201
+ }
202
+
203
+ public void SetInstanceIdleTimeout_ResetEnabled(string? value)
204
+ {
205
+ if (Int32.TryParse(value, out Int32 parseResult))
206
+ {
207
+ InstanceIdleTimeout_ResetEnabled = !Equals(_defaultInstanceIdleTimeout, parseResult);
208
+ }
209
+ else
210
+ {
211
+ InstanceIdleTimeout_ResetEnabled = true;
212
+ }
213
+ }
214
+
215
+ private bool _instanceIdleTimeout_ResetEnabled;
216
+
217
+ public bool InstanceIdleTimeout_ResetEnabled
218
+ {
219
+ get => _instanceIdleTimeout_ResetEnabled;
220
+ set => SetProperty(ref _instanceIdleTimeout_ResetEnabled, value);
221
+ }
222
+
223
+ private void InstanceIdleTimeout_ResetExecuted(string? param)
224
+ {
225
+ InstanceIdleTimeout = _defaultInstanceIdleTimeout.ToString();
226
+ }
227
+
228
+ public ICommand InstanceIdleTimeout_ResetCommand => new RelayCommand<string>(InstanceIdleTimeout_ResetExecuted);
229
}
\ No newline at end of file
src/windows/wslsettings/Views/Settings/OptionalFeaturesPage.xaml
+18
-1
@@ -41,7 +41,10 @@
41
<ctControls:SettingsCard x:Uid="Settings_SparseVHD">
42
<ToggleSwitch x:Uid="Settings_SparseVHDToggleSwitch" HorizontalAlignment="Right" MinWidth="0" IsOn="{x:Bind ViewModel.IsOnSparseVHD, Mode=TwoWay}"/>
43
</ctControls:SettingsCard>
44
- <ctControls:SettingsExpander x:Name="VMIdleTimeoutExpander" x:Uid="Settings_VMIdleTimeout">
44
+ <ctControls:SettingsCard x:Uid="Settings_KeepWslRunning">
45
+ <ToggleSwitch x:Uid="Settings_KeepWslRunningToggleSwitch" HorizontalAlignment="Right" MinWidth="0" IsOn="{x:Bind ViewModel.IsOnKeepWslRunning, Mode=TwoWay}"/>
46
+ </ctControls:SettingsCard>
47
+ <ctControls:SettingsExpander x:Name="VMIdleTimeoutExpander" x:Uid="Settings_VMIdleTimeout" IsEnabled="{x:Bind ViewModel.VMIdleTimeoutEnabled, Mode=OneWay}">
48
<TextBlock Style="{StaticResource TextBlockSettingStyle}" Text="{x:Bind ViewModel.VMIdleTimeout, Mode=OneWay, Converter={StaticResource MillisecondsStringConverter}}"/>
49
<ctControls:SettingsExpander.Items>
50
<ctControls:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left" Margin="{StaticResource SettingsExpanderItemMargin}">
@@ -55,6 +58,20 @@
58
</ctControls:SettingsCard>
59
</ctControls:SettingsExpander.Items>
60
</ctControls:SettingsExpander>
61
+ <ctControls:SettingsExpander x:Name="InstanceIdleTimeoutExpander" x:Uid="Settings_InstanceIdleTimeout" IsEnabled="{x:Bind ViewModel.InstanceIdleTimeoutEnabled, Mode=OneWay}">
62
+ <TextBlock Style="{StaticResource TextBlockSettingStyle}" Text="{x:Bind ViewModel.InstanceIdleTimeout, Mode=OneWay, Converter={StaticResource MillisecondsStringConverter}}"/>
63
+ <ctControls:SettingsExpander.Items>
64
+ <ctControls:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left" Margin="{StaticResource SettingsExpanderItemMargin}">
65
+ <StackPanel Orientation="Horizontal">
66
+ <TextBox x:Name="InstanceIdleTimeoutTextBox" x:Uid="Settings_InstanceIdleTimeoutTextBox" Style="{StaticResource TextBoxFilePathStyle}" Margin="{StaticResource InputControlSpacingMargin}"
67
+ Text="{x:Bind ViewModel.InstanceIdleTimeout, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" TextChanged="InstanceIdleTimeoutTextBox_TextChanged"/>
68
+ <Button x:Uid="Settings_InstanceIdleTimeoutResetButton" Style="{StaticResource ButtonSettingStyle}" Margin="{StaticResource InputControlSpacingMargin}"
69
+ Command="{x:Bind ViewModel.InstanceIdleTimeout_ResetCommand}" IsEnabled="{x:Bind ViewModel.InstanceIdleTimeout_ResetEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
70
+ Click="Settings_ResetButton_Click"/>
71
+ </StackPanel>
72
+ </ctControls:SettingsCard>
73
+ </ctControls:SettingsExpander.Items>
74
+ </ctControls:SettingsExpander>
75
</StackPanel>
76
</ScrollViewer>
77
</Grid>
src/windows/wslsettings/Views/Settings/OptionalFeaturesPage.xaml.cs
+11
@@ -35,6 +35,7 @@ public sealed partial class OptionalFeaturesPage : Page
35
{
36
OptionalFeaturesPageRoot.Focus(FocusState.Programmatic);
37
RuntimeHelper.SetupExpanderFocusManagementByName(this, "VMIdleTimeoutExpander", "VMIdleTimeoutTextBox");
38
+ RuntimeHelper.SetupExpanderFocusManagementByName(this, "InstanceIdleTimeoutExpander", "InstanceIdleTimeoutTextBox");
39
}
40
41
override protected void OnNavigatedFrom(NavigationEventArgs e)
@@ -62,4 +63,14 @@ public sealed partial class OptionalFeaturesPage : Page
63
TextBox? textBox = sender as TextBox;
64
ViewModel.SetVMIdleTimeout_ResetEnabled(textBox!.Text);
65
}
66
+
67
+ private void InstanceIdleTimeoutTextBox_TextChanged(object sender, TextChangedEventArgs e)
68
+ {
69
+ if (sender is not TextBox textBox)
70
+ {
71
+ return;
72
+ }
73
+
74
+ ViewModel.SetInstanceIdleTimeout_ResetEnabled(textBox.Text);
75
+ }
76
}
src/windows/wslsettings/Views/Settings/SettingsApplyHelper.cs
+2
@@ -156,6 +156,7 @@ internal static class SettingsApplyHelper
156
{ WslConfigEntry.SafeModeEnabled, "Settings_SafeMode/Header" },
157
{ WslConfigEntry.SparseVHDEnabled, "Settings_SparseVHD/Header" },
158
{ WslConfigEntry.VMIdleTimeout, "Settings_VMIdleTimeout/Header" },
159
+ { WslConfigEntry.InstanceIdleTimeout, "Settings_InstanceIdleTimeout/Header" },
160
{ WslConfigEntry.DebugConsoleEnabled, "Settings_DebugConsole/Header" },
161
{ WslConfigEntry.HardwarePerformanceCountersEnabled, "Settings_HWPerfCounters/Header" },
162
{ WslConfigEntry.KernelPath, "Settings_CustomKernelPath/Header" },
@@ -173,6 +174,7 @@ internal static class SettingsApplyHelper
174
switch (entry)
175
{
176
case WslConfigEntry.InitialAutoProxyTimeout:
177
+ case WslConfigEntry.InstanceIdleTimeout:
178
case WslConfigEntry.VMIdleTimeout:
179
return string.Format("Settings_MillisecondsStringFormat".GetLocalized(), (int)value);
180
default: