Skip to content

Commit

Permalink
Remove Worker.Core assembly from DOTNET_STARTUP_HOOKS at startup (#1539)
Browse files Browse the repository at this point in the history
  • Loading branch information
jviau authored and satvu committed Jun 8, 2023
1 parent f00e93b commit 8726761
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 2 deletions.
2 changes: 1 addition & 1 deletion release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

### Microsoft.Azure.Functions.Worker.Core <version>

- <event>
- Fixed issue spawning child process while debugging due to "DOTNET_STARTUP_HOOKS" always containing "Microsoft.Azure.Functions.Worker.Core". (#1539)

### Microsoft.Azure.Functions.Worker.Grpc <version>

Expand Down
25 changes: 25 additions & 0 deletions src/DotNetWorker.Core/StartupHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;

/// <summary>
Expand All @@ -16,12 +19,17 @@
/// </summary>
internal class StartupHook
{
const string StartupHooksEnvVar = "DOTNET_STARTUP_HOOKS";
private static readonly string _startupSeparator = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ";" : ":";
private static readonly string? _assemblyName = typeof(StartupHook).Assembly.GetName().Name;

public static void Initialize()
{
// Time to wait between checks, in ms.
const int SleepTime = 500;
const int MaxWaitCycles = (60 * 1000) / SleepTime;

RemoveSelfFromStartupHooks();
string? debuggerWaitEnabled = Environment.GetEnvironmentVariable("FUNCTIONS_ENABLE_DEBUGGER_WAIT");
string? jsonOutputEnabled = Environment.GetEnvironmentVariable("FUNCTIONS_ENABLE_JSON_OUTPUT");
#if NET5_0_OR_GREATER
Expand Down Expand Up @@ -62,4 +70,21 @@ static bool WaitOnDebugger(int cycle)
}
}
}

internal static void RemoveSelfFromStartupHooks()
{
string? startupHooks = Environment.GetEnvironmentVariable(StartupHooksEnvVar);
if (string.IsNullOrEmpty(startupHooks))
{
// If this call happened, we are clearly part of this environment variable.
// This is mostly to make strict-nulls happy.
return;
}

// netstandard2.0 has no StringSplitOptions overload.
IEnumerable<string> parts = startupHooks.Split(_startupSeparator[0])
.Where(x => !string.Equals(x, _assemblyName, StringComparison.Ordinal));
string newValue = string.Join(_startupSeparator, parts);
Environment.SetEnvironmentVariable(StartupHooksEnvVar, newValue);
}
}
37 changes: 36 additions & 1 deletion test/DotNetWorkerTests/StartupHookTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Xunit;

namespace Microsoft.Azure.Functions.Worker.Tests
{
public class StartupHookTests
{
[Fact]
public void ValidadeHookSetup()
public void ValidateHookSetup()
{
var hookType = typeof(StartupHook);
var initializeMethod = hookType.GetMethod("Initialize", BindingFlags.Static | BindingFlags.Public);
Expand All @@ -24,5 +27,37 @@ public void ValidadeHookSetup()
Assert.Empty(initializeMethod.GetParameters());
Assert.Equal(typeof(void), initializeMethod.ReturnType);
}

[Theory]
[InlineData(null)]
[InlineData("Microsoft.Azure.Functions.Worker.Core")]
[InlineData("Some.Other.Assembly")]
[InlineData("Microsoft.Azure.Functions.Worker.Core", "Some.Other.Assembly")]
[InlineData("Some.Other.Assembly", "Microsoft.Azure.Functions.Worker.Core")]
[InlineData("Some.Other.Assembly", "Microsoft.Azure.Functions.Worker.Core", "Another.Assembly")]
public void ValidateHookSetup_HookVariableRemoved(params string[] hooks)
{
hooks ??= Array.Empty<string>();
char separator = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ';' : ':';
string envVar = "DOTNET_STARTUP_HOOKS";
string original = Environment.GetEnvironmentVariable(envVar);

try
{
IEnumerable<string> expected = hooks.Where(
x => x != typeof(StartupHook).Assembly.GetName().Name);
Environment.SetEnvironmentVariable(envVar, string.Join(separator, hooks));
StartupHook.RemoveSelfFromStartupHooks();

string value = Environment.GetEnvironmentVariable(envVar);
IEnumerable<string> actual = string.IsNullOrEmpty(value)
? Enumerable.Empty<string>() : value.Split(separator);
Assert.Equal(expected, actual);
}
finally
{
Environment.SetEnvironmentVariable(envVar, original);
}
}
}
}

0 comments on commit 8726761

Please sign in to comment.