@samitouri / QOSAMI-WSL / commits / 2c93143e

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: