Skip to content
Merged
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
24 changes: 20 additions & 4 deletions src/System.Management.Automation/utils/Telemetry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,15 +1009,31 @@ private static Guid GetUniqueIdentifier()
try
{
// CreateUniqueIdentifierAndFile shouldn't throw, but the mutex might
using var m = new Mutex(true, "CreateUniqueUserId");
m.WaitOne();
using var mutex = new Mutex(initiallyOwned: false, name: "CreateUniqueUserId");
bool mutexAcquired = false;
try
{
return CreateUniqueIdentifierAndFile(s_uuidPath);
try
{
mutexAcquired = mutex.WaitOne(millisecondsTimeout: 200);
}
catch (AbandonedMutexException)
{
// WaitOne transfers ownership before throwing for an abandoned mutex.
mutexAcquired = true;
}

if (mutexAcquired)
{
return CreateUniqueIdentifierAndFile(s_uuidPath);
}
}
finally
{
m.ReleaseMutex();
if (mutexAcquired)
{
mutex.ReleaseMutex();
}
}
}
catch (Exception)
Expand Down
111 changes: 111 additions & 0 deletions test/powershell/engine/Basic/Telemetry.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,22 @@ Describe "Telemetry for shell startup" -Tag CI {
$PWSH = (Get-Process -Id $PID).MainModule.FileName
$telemetrySet = Test-Path -Path env:POWERSHELL_TELEMETRY_OPTOUT
$SendingTelemetry = $env:POWERSHELL_TELEMETRY_OPTOUT

function Invoke-TelemetryTestProcess {
param(
[Parameter(Mandatory)]
[string[]] $Argument
)

$startInfo = [System.Diagnostics.ProcessStartInfo]::new()
$startInfo.FileName = $PWSH
$startInfo.UseShellExecute = $false
foreach ($argumentValue in $Argument) {
$startInfo.ArgumentList.Add($argumentValue)
}

return [System.Diagnostics.Process]::Start($startInfo)
}
}

AfterAll {
Expand Down Expand Up @@ -108,6 +124,101 @@ Describe "Telemetry for shell startup" -Tag CI {
$uuidPath | Should -Exist
}

It "Should not block concurrent telemetry-enabled shell startup when the uuid file is missing" {
$env:POWERSHELL_TELEMETRY_OPTOUT = "no"
$holderReadyPath = Join-Path -Path $TestDrive -ChildPath "telemetry-holder.ready"
$holderStopPath = Join-Path -Path $TestDrive -ChildPath "telemetry-holder.stop"
$holderScriptPath = Join-Path -Path $TestDrive -ChildPath "telemetry-holder.ps1"
@'
param($ReadyPath, $StopPath)

[void][Microsoft.PowerShell.Telemetry.ApplicationInsightsTelemetry]::CanSendTelemetry
[System.IO.File]::WriteAllText($ReadyPath, "")
while (-not [System.IO.File]::Exists($StopPath)) {
Start-Sleep -Milliseconds 100
}
'@ | Set-Content -LiteralPath $holderScriptPath

$holderProcess = $null
$childProcess = $null
try {
$holderProcess = Invoke-TelemetryTestProcess -Argument @(
"-NoProfile",
"-File",
$holderScriptPath,
$holderReadyPath,
$holderStopPath
)

Wait-UntilTrue -sb { Test-Path -LiteralPath $holderReadyPath } -TimeoutInMilliseconds 15000 -IntervalInMilliseconds 100 |
Should -BeTrue
$uuidPath | Should -Exist
Remove-Item -LiteralPath $uuidPath

$childProcess = Invoke-TelemetryTestProcess -Argument @("-NoProfile", "-Command", "exit")
$childProcess.WaitForExit(15000) | Should -BeTrue
$childProcess.ExitCode | Should -Be 0
$uuidPath | Should -Exist
}
finally {
if ($childProcess -and -not $childProcess.HasExited) {
$childProcess.Kill($true)
$childProcess.WaitForExit()
}

if ($childProcess) {
$childProcess.Dispose()
}

[System.IO.File]::WriteAllText($holderStopPath, "")
if ($holderProcess -and -not $holderProcess.WaitForExit(5000)) {
$holderProcess.Kill($true)
$holderProcess.WaitForExit()
}

if ($holderProcess) {
$holderProcess.Dispose()
}
}
}

It "Should stop creating the uuid file when the telemetry mutex times out" {
$env:POWERSHELL_TELEMETRY_OPTOUT = "no"
$mutex = [System.Threading.Mutex]::new($false, "CreateUniqueUserId")
$mutexAcquired = $false
$childProcess = $null
try {
try {
$mutexAcquired = $mutex.WaitOne(5000)
}
catch [System.Threading.AbandonedMutexException] {
$mutexAcquired = $true
}

$mutexAcquired | Should -BeTrue -Because "the test must own the mutex to force the child to time out"
$childProcess = Invoke-TelemetryTestProcess -Argument @("-NoProfile", "-Command", "exit")
$childProcess.WaitForExit(15000) | Should -BeTrue
$childProcess.ExitCode | Should -Be 0
$uuidPath | Should -Not -Exist
}
finally {
if ($childProcess -and -not $childProcess.HasExited) {
$childProcess.Kill($true)
$childProcess.WaitForExit()
}

if ($childProcess) {
$childProcess.Dispose()
}

if ($mutexAcquired) {
$mutex.ReleaseMutex()
}

$mutex.Dispose()
}
}

It "Should create a uuid file by default" {
if ( Test-Path env:POWERSHELL_TELEMETRY_OPTOUT ) { Remove-Item -Path env:POWERSHELL_TELEMETRY_OPTOUT }
& $PWSH -NoProfile -Command "exit"
Expand Down
Loading