Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions localization/strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -1985,6 +1985,18 @@ You can also access more VS Code Remote options through the command palette with
<data name="Settings_VMIdleTimeout.Description" xml:space="preserve">
<value>The number of milliseconds that a VM is idle, before it is shut down.</value>
</data>
<data name="Settings_KeepWslRunning.Header" xml:space="preserve">
<value>Keep WSL running</value>
</data>
<data name="Settings_KeepWslRunning.Description" xml:space="preserve">
<value>Keep running distributions, and the WSL virtual machine hosting them, from being automatically shut down after they have been idle.</value>
</data>
<data name="Settings_KeepWslRunningToggleSwitch.AutomationProperties.Name" xml:space="preserve">
<value>Keep WSL running.</value>
</data>
<data name="Settings_KeepWslRunningToggleSwitch.AutomationProperties.HelpText" xml:space="preserve">
<value>Keep running distributions, and the WSL virtual machine hosting them, from being automatically shut down after they have been idle.</value>
</data>
<data name="Settings_VMIdleTimeoutResetButton.Content" xml:space="preserve">
<value>Reset timeout</value>
</data>
Expand All @@ -1994,6 +2006,21 @@ You can also access more VS Code Remote options through the command palette with
<data name="Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText" xml:space="preserve">
<value>The number of milliseconds that a VM is idle, before it is shut down.</value>
</data>
<data name="Settings_InstanceIdleTimeout.Header" xml:space="preserve">
<value>Distribution idle timeout</value>
</data>
<data name="Settings_InstanceIdleTimeout.Description" xml:space="preserve">
<value>The number of milliseconds that a distribution is idle, before it is terminated.</value>
</data>
<data name="Settings_InstanceIdleTimeoutResetButton.Content" xml:space="preserve">
<value>Reset timeout</value>
</data>
<data name="Settings_InstanceIdleTimeoutTextBox.AutomationProperties.Name" xml:space="preserve">
<value>Distribution idle timeout</value>
</data>
<data name="Settings_InstanceIdleTimeoutTextBox.AutomationProperties.HelpText" xml:space="preserve">
<value>The number of milliseconds that a distribution is idle, before it is terminated.</value>
</data>
<data name="Settings_ApplyChangesButton.Content" xml:space="preserve">
<value>Apply changes</value>
</data>
Expand Down
1 change: 1 addition & 0 deletions src/windows/inc/WslCoreConfigInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ enum WslConfigEntry
KernelPath,
SystemDistroPath,
KernelModulesPath,
InstanceIdleTimeout,
};

enum NetworkingConfiguration
Expand Down
11 changes: 11 additions & 0 deletions src/windows/libwsl/WslCoreConfigInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ WslConfigSetting GetWslConfigSetting(WslConfig_t wslConfig, WslConfigEntry wslCo
static_assert(std::is_same<decltype(wslConfigSetting.StringValue), decltype(wslConfig->Config.KernelModulesPath.c_str())>::value);
wslConfigSetting.StringValue = wslConfig->Config.KernelModulesPath.c_str();
break;
case InstanceIdleTimeout:
static_assert(std::is_same<decltype(wslConfigSetting.Int32Value), decltype(wslConfig->Config.InstanceIdleTimeout)>::value);
wslConfigSetting.Int32Value = wslConfig->Config.InstanceIdleTimeout;
break;
default:
FAIL_FAST();
}
Expand Down Expand Up @@ -560,6 +564,13 @@ unsigned long SetWslConfigSetting(WslConfig_t wslConfig, WslConfigSetting wslCon
wslConfigSetting.StringValue,
wslConfig->Config.KernelModulesPath);
}
case InstanceIdleTimeout:
return SetWslConfigSetting(
wslConfig,
ConfigSetting::InstanceIdleTimeout,
defaultConfig.InstanceIdleTimeout,
wslConfigSetting.Int32Value,
wslConfig->Config.InstanceIdleTimeout);
default:
FAIL_FAST();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ WslConfigEntry.SystemDistroPath or

WslConfigEntry.ProcessorCount or
WslConfigEntry.InitialAutoProxyTimeout or
WslConfigEntry.InstanceIdleTimeout or
WslConfigEntry.VMIdleTimeout => WslConfigValueKind.Int32,

WslConfigEntry.MemorySizeBytes or
Expand Down
3 changes: 2 additions & 1 deletion src/windows/wslsettings/LibWsl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public enum WslConfigEntry
HardwarePerformanceCountersEnabled = 23,
KernelPath = 24,
SystemDistroPath = 25,
KernelModulesPath = 26
KernelModulesPath = 26,
InstanceIdleTimeout = 27
}
Comment thread
craigloewen-msft marked this conversation as resolved.

public enum NetworkingConfiguration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ public partial class OptionalFeaturesViewModel : WslConfigSettingViewModel
private IWslConfigSetting? _sparseVHD;
private IWslConfigSetting? _vMIdleTimeout;
private int _defaultVMIdleTimeout;
private IWslConfigSetting? _instanceIdleTimeout;
private int _defaultInstanceIdleTimeout;
private int _previousInstanceIdleTimeout;

public OptionalFeaturesViewModel()
{
InitializeConfigSettings();

VMIdleTimeout_ResetEnabled = !Equals(_defaultVMIdleTimeout, _vMIdleTimeout!.Int32Value);
InstanceIdleTimeout_ResetEnabled = !Equals(_defaultInstanceIdleTimeout, _instanceIdleTimeout!.Int32Value);
}

protected override void InitializeConfigSettings()
Expand All @@ -33,8 +37,13 @@ protected override void InitializeConfigSettings()
_safeMode = wslConfigService.GetWslConfigSetting(WslConfigEntry.SafeModeEnabled);
_sparseVHD = wslConfigService.GetWslConfigSetting(WslConfigEntry.SparseVHDEnabled);
_vMIdleTimeout = wslConfigService.GetWslConfigSetting(WslConfigEntry.VMIdleTimeout);
_instanceIdleTimeout = wslConfigService.GetWslConfigSetting(WslConfigEntry.InstanceIdleTimeout);

_defaultVMIdleTimeout = wslConfigService.GetWslConfigSetting(WslConfigEntry.VMIdleTimeout, true).Int32Value;
_defaultInstanceIdleTimeout = wslConfigService.GetWslConfigSetting(WslConfigEntry.InstanceIdleTimeout, true).Int32Value;

var configuredInstanceIdleTimeout = _instanceIdleTimeout!.Int32Value;
_previousInstanceIdleTimeout = configuredInstanceIdleTimeout < 0 ? _defaultInstanceIdleTimeout : configuredInstanceIdleTimeout;
}

public List<string> MemoryReclaimModes
Expand Down Expand Up @@ -72,6 +81,46 @@ public bool IsOnSparseVHD
set { Set(ref _sparseVHD!, value); }
}

// Keeping WSL running is driven by the distribution idle timeout: a negative value tells the
// service to never idle-terminate a distribution, and because the VM is only considered idle
// once every distribution has stopped, this keeps the WSL VM running as well.
public bool IsOnKeepWslRunning
{
get { return _instanceIdleTimeout!.Int32Value < 0; }
set
{
if (value)
{
var currentInstanceIdleTimeout = _instanceIdleTimeout!.Int32Value;
if (currentInstanceIdleTimeout >= 0)
{
_previousInstanceIdleTimeout = currentInstanceIdleTimeout;
}

Set(ref _instanceIdleTimeout!, -1, nameof(IsOnKeepWslRunning));
}
else
{
Set(ref _instanceIdleTimeout!, _previousInstanceIdleTimeout, nameof(IsOnKeepWslRunning));
}

OnPropertyChanged(nameof(InstanceIdleTimeout));
Comment thread
craigloewen-msft marked this conversation as resolved.
OnPropertyChanged(nameof(InstanceIdleTimeoutEnabled));
OnPropertyChanged(nameof(VMIdleTimeoutEnabled));
InstanceIdleTimeout_ResetEnabled = !Equals(_defaultInstanceIdleTimeout, _instanceIdleTimeout!.Int32Value);
}
}

public bool VMIdleTimeoutEnabled
{
get { return !IsOnKeepWslRunning; }
}

public bool InstanceIdleTimeoutEnabled
{
get { return !IsOnKeepWslRunning; }
}
Comment thread
craigloewen-msft marked this conversation as resolved.

public string VMIdleTimeout
{
get
Expand Down Expand Up @@ -120,4 +169,61 @@ private void VMIdleTimeout_ResetExecuted(string? param)
}

public ICommand VMIdleTimeout_ResetCommand => new RelayCommand<string>(VMIdleTimeout_ResetExecuted);

public string InstanceIdleTimeout
{
get
{
return _instanceIdleTimeout!.Int32Value.ToString();
}
set
{
if (ValidateInput(value, Constants.IntegerRegex))
{
if (Int32.TryParse(value, out int parsedValue))
{
Set(ref _instanceIdleTimeout!, parsedValue);
if (parsedValue >= 0)
{
_previousInstanceIdleTimeout = parsedValue;
}

OnPropertyChanged(nameof(IsOnKeepWslRunning));
OnPropertyChanged(nameof(InstanceIdleTimeoutEnabled));
OnPropertyChanged(nameof(VMIdleTimeoutEnabled));
}
else
{
OnPropertyChanged();
}
}
}
}

public void SetInstanceIdleTimeout_ResetEnabled(string? value)
{
if (Int32.TryParse(value, out Int32 parseResult))
{
InstanceIdleTimeout_ResetEnabled = !Equals(_defaultInstanceIdleTimeout, parseResult);
}
else
{
InstanceIdleTimeout_ResetEnabled = true;
}
Comment thread
craigloewen-msft marked this conversation as resolved.
}

private bool _instanceIdleTimeout_ResetEnabled;

public bool InstanceIdleTimeout_ResetEnabled
{
get => _instanceIdleTimeout_ResetEnabled;
set => SetProperty(ref _instanceIdleTimeout_ResetEnabled, value);
}

private void InstanceIdleTimeout_ResetExecuted(string? param)
{
InstanceIdleTimeout = _defaultInstanceIdleTimeout.ToString();
}

public ICommand InstanceIdleTimeout_ResetCommand => new RelayCommand<string>(InstanceIdleTimeout_ResetExecuted);
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@
<ctControls:SettingsCard x:Uid="Settings_SparseVHD">
<ToggleSwitch x:Uid="Settings_SparseVHDToggleSwitch" HorizontalAlignment="Right" MinWidth="0" IsOn="{x:Bind ViewModel.IsOnSparseVHD, Mode=TwoWay}"/>
</ctControls:SettingsCard>
<ctControls:SettingsExpander x:Name="VMIdleTimeoutExpander" x:Uid="Settings_VMIdleTimeout">
<ctControls:SettingsCard x:Uid="Settings_KeepWslRunning">
<ToggleSwitch x:Uid="Settings_KeepWslRunningToggleSwitch" HorizontalAlignment="Right" MinWidth="0" IsOn="{x:Bind ViewModel.IsOnKeepWslRunning, Mode=TwoWay}"/>
</ctControls:SettingsCard>
<ctControls:SettingsExpander x:Name="VMIdleTimeoutExpander" x:Uid="Settings_VMIdleTimeout" IsEnabled="{x:Bind ViewModel.VMIdleTimeoutEnabled, Mode=OneWay}">
<TextBlock Style="{StaticResource TextBlockSettingStyle}" Text="{x:Bind ViewModel.VMIdleTimeout, Mode=OneWay, Converter={StaticResource MillisecondsStringConverter}}"/>
Comment thread
craigloewen-msft marked this conversation as resolved.
<ctControls:SettingsExpander.Items>
<ctControls:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left" Margin="{StaticResource SettingsExpanderItemMargin}">
Expand All @@ -55,6 +58,20 @@
</ctControls:SettingsCard>
</ctControls:SettingsExpander.Items>
</ctControls:SettingsExpander>
<ctControls:SettingsExpander x:Name="InstanceIdleTimeoutExpander" x:Uid="Settings_InstanceIdleTimeout" IsEnabled="{x:Bind ViewModel.InstanceIdleTimeoutEnabled, Mode=OneWay}">
<TextBlock Style="{StaticResource TextBlockSettingStyle}" Text="{x:Bind ViewModel.InstanceIdleTimeout, Mode=OneWay, Converter={StaticResource MillisecondsStringConverter}}"/>
Comment thread
craigloewen-msft marked this conversation as resolved.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the toggle is on, InstanceIdleTimeout is -1, and MillisecondsStringConverter only special-cases ulong 0, so this collapsed summary ends up reading literally -1 ms. Same thing shows up in the apply-changes dialog, since SettingsApplyHelper.FormatValue runs the new entry through Settings_MillisecondsStringFormat - so toggling Keep WSL VM alive produces a confirmation line like Distribution idle timeout: -1 ms for a field the user never touched.

Could we have the converter return null (or something like Never) for negative values, and map the toggle to Settings_KeepVMAlive/Header in the apply summary?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually prefer it showing -1 as it is clearly communicating what it's setting. I think changing it to show 'Never' while making it more readable, makes it less clear on what actual settings are changing.
So I'd prefer to stick with displaying it as -1 as that is what gets truly set in the file.

<ctControls:SettingsExpander.Items>
<ctControls:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left" Margin="{StaticResource SettingsExpanderItemMargin}">
<StackPanel Orientation="Horizontal">
<TextBox x:Name="InstanceIdleTimeoutTextBox" x:Uid="Settings_InstanceIdleTimeoutTextBox" Style="{StaticResource TextBoxFilePathStyle}" Margin="{StaticResource InputControlSpacingMargin}"
Text="{x:Bind ViewModel.InstanceIdleTimeout, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" TextChanged="InstanceIdleTimeoutTextBox_TextChanged"/>
<Button x:Uid="Settings_InstanceIdleTimeoutResetButton" Style="{StaticResource ButtonSettingStyle}" Margin="{StaticResource InputControlSpacingMargin}"
Command="{x:Bind ViewModel.InstanceIdleTimeout_ResetCommand}" IsEnabled="{x:Bind ViewModel.InstanceIdleTimeout_ResetEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Click="Settings_ResetButton_Click"/>
</StackPanel>
</ctControls:SettingsCard>
</ctControls:SettingsExpander.Items>
</ctControls:SettingsExpander>
</StackPanel>
</ScrollViewer>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ private void OnPageLoaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
OptionalFeaturesPageRoot.Focus(FocusState.Programmatic);
RuntimeHelper.SetupExpanderFocusManagementByName(this, "VMIdleTimeoutExpander", "VMIdleTimeoutTextBox");
RuntimeHelper.SetupExpanderFocusManagementByName(this, "InstanceIdleTimeoutExpander", "InstanceIdleTimeoutTextBox");
}

override protected void OnNavigatedFrom(NavigationEventArgs e)
Expand Down Expand Up @@ -62,4 +63,14 @@ private void VMIdleTimeoutTextBox_TextChanged(object sender, TextChangedEventArg
TextBox? textBox = sender as TextBox;
ViewModel.SetVMIdleTimeout_ResetEnabled(textBox!.Text);
}

private void InstanceIdleTimeoutTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (sender is not TextBox textBox)
{
return;
}

ViewModel.SetInstanceIdleTimeout_ResetEnabled(textBox.Text);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ private static string GetSettingDisplayName(WslConfigEntry entry)
{ WslConfigEntry.SafeModeEnabled, "Settings_SafeMode/Header" },
{ WslConfigEntry.SparseVHDEnabled, "Settings_SparseVHD/Header" },
{ WslConfigEntry.VMIdleTimeout, "Settings_VMIdleTimeout/Header" },
{ WslConfigEntry.InstanceIdleTimeout, "Settings_InstanceIdleTimeout/Header" },
{ WslConfigEntry.DebugConsoleEnabled, "Settings_DebugConsole/Header" },
{ WslConfigEntry.HardwarePerformanceCountersEnabled, "Settings_HWPerfCounters/Header" },
{ WslConfigEntry.KernelPath, "Settings_CustomKernelPath/Header" },
Expand All @@ -173,6 +174,7 @@ private static string FormatValue(WslConfigEntry entry, object value)
switch (entry)
{
case WslConfigEntry.InitialAutoProxyTimeout:
case WslConfigEntry.InstanceIdleTimeout:
case WslConfigEntry.VMIdleTimeout:
return string.Format("Settings_MillisecondsStringFormat".GetLocalized(), (int)value);
default:
Comment thread
craigloewen-msft marked this conversation as resolved.
Expand Down