Skip to content

[Breaking change]: dotnet.acquire API for VS Code No Longer Always Downloads Latest #49127

@nagilson

Description

@nagilson

Description

As implemented in dotnet/vscode-dotnet-runtime#2420 and described in dotnet/vscode-dotnet-runtime#2359, The .NET Install Tool Extension (https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.vscode-dotnet-runtime) for VS Code Extension Developers provides the VS Code command dotnet.acquire to install a .NET runtime given a major.minor, amongst other information such as an architecture. Some of the commands are documented at https://github.com/dotnet/vscode-dotnet-runtime/blob/main/Documentation/commands.md.

Example of how a VS Code Extension may install the runtime using our extension:
const dotnetRuntimePath = (await vscode.commands.executeCommand<IDotnetAcquireResult>('dotnet.acquire', { version: '9.0', 'requestingExtensionId'})).dotnetPath;

The commands for dotnet.acquire and dotnet.uninstall were both changed. dotnet.acquire no longer downloads the latest runtime each time and will instead periodically manage, update, and uninstall runtime versions. dotnet.uninstall will no longer allow uninstalling an install that is 'in-use' which is defined below.

Version

Other (please put exact version in description textbox)

Previous behavior

Up until version 3.0.0 of the .NET Install Tool, or pre-release version 2.4.1, calling dotnet.acquire would always trigger a check to see what the latest runtime version was, given the user was online and did not specify any settings to do otherwise. If that latest runtime was not available on the machine, it would be installed, and the path to it would be returned.

This runtime is used for C#DevKit, C#, and other VS Code extensions, both first and third party, to run internal C# code/processes, such as the language server host. This runtime is generally not used to run the users specific project, and is a private, user-folder copy on disk. It may be the runtime or aspnetcore runtime.

A similar command, dotnet.uninstall exists and is also documented above. That command would uninstall any dotnet runtime or SDK that an extension had requested if no other extensions depended upon that installation. By depend on, we mean that an extension had requested that version of .NET in the past. This was a reference count-based mechanism to allow uninstallation when no further dependent extensions of an install remained.

New behavior

As part of a change to improve performance and management of the .NET Runtime:

  1. dotnet.acquire will no longer always check for a newer runtime version before returning path to a runtime that matches the major.minor. It will instead use existing runtime installations it's made and check daily for a new runtime after runtimeUpdateDelaySeconds, which defaults to 5 minutes. This is a setting that can be changed in the VS Code Extension Settings. After this time, the prior check will commence for newer runtime versions, and the new runtime will be installed if needed. After this, all remaining .NET runtimes that are not 'in-use' and are not the latest patch for a specific major.minor, architecture, and mode (runtime vs aspnetcore [runtime]) combo will be uninstalled automatically.

    'in-use' means that the executable path to that .NET runtime install was:

    a. Returned by a command through the .NET Install Tool in any session of vscode, vscode insiders, or otherwise that has a currently live, running process. This includes but is not limited to: dotnet.acquire, dotnet.acquireStatus, dotnet.availableInstalls, and dotnet.findPath.

    b. Part of the PATH or DOTNET_ROOT environment variable in which VS Code is operating within.

  2. dotnet.uninstall will not uninstall if the install is considered to be 'in-use'. This command already rejected uninstalls that were made while the dotnet.exe corresponding to that installation folder had a conflicting file handle that demonstrated it was running/in-use. This was determined by trying to open the executable with O_RDONLY permissions or FILE_FLAG_NO_BUFFERING on windows, and by checking for EBUSY, EACCESS, or processes via lsof on Unix.

Type of breaking change

  • Binary incompatible: Existing binaries might encounter a breaking change in behavior, such as failure to load or execute, and if so, require recompilation.
  • Source incompatible: When recompiled using the new SDK or component or to target the new runtime, existing source code might require source changes to compile successfully.
  • Behavioral change: Existing binaries might behave differently at run time.

Reason for change

This speeds up the developer loop by deferring the network call and installation time away from the launch of extensions that utilize dotnet. For users in the worst 3% of networks who did not have their own .NET Runtime that matched the requirements of any extensions .NET Version, the download time alone created a 9-36 second delay at startup. For the median user this represented a 287ms delay.

Recommended action

Developers who rely on dotnet.acquire and want to enforce that a new runtime is used every single time should consider using forceUpdate : true in their call to dotnet.acquire. We generally recommend against this unless for a short period in which a runtime security patch, feature, or bug is dramatic enough to warrant downloading the latest runtime.

Developers who rely on the .NET Install Tool should make sure that they don't cache / re-use any path from the .NET Install Tool without calling dotnet.availableInstalls again to demonstrate that they are using the installation in a given session if it's not in the PATH.

Developers should continue to use dotnet.uninstall as they previously did to decrement the reference counts of the installs they own. However, this is less vital since uninstallation happens when the older version is replaced with the newer version.

Customers should avoid relying on using hard-coded private runtime installations managed by the .NET Install Tool as that is not the intended purpose of these runtimes. If they must, they should set the PATH or DOTNET_ROOT for their vscode instances such that the install does not get automatically cleaned up.

Feature area

Extensions

Affected APIs

No response

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status

🔖 Ready

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions