Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Worker.Core assembly from DOTNET_STARTUP_HOOKS at startup #1539

Merged
merged 6 commits into from
May 24, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
- Update Microsoft.Azure.Functions.Worker.Grpc dependency to 1.10.1
### Microsoft.Azure.Functions.Worker.Core 1.12.1
- Minor documentation updates (no functional changes)
- 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 1.10.1
- Fixed an issue causing throughput degradation and for synchronous functions, blocked the execution pipeline. (#1516)
26 changes: 26 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 char s_startupSeparator = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ';' : ':';
jviau marked this conversation as resolved.
Show resolved Hide resolved
private static readonly string? s_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,22 @@ 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(s_startupSeparator).Where(x => !string.IsNullOrEmpty(x));
string newValue = string.Join(
s_startupSeparator.ToString(), // netstandard2.0 only accepts string here.
parts.Where(x => !x.Equals(s_assemblyName, StringComparison.Ordinal)));
jviau marked this conversation as resolved.
Show resolved Hide resolved
Environment.SetEnvironmentVariable(StartupHooksEnvVar, newValue);
}
}
35 changes: 35 additions & 0 deletions test/DotNetWorkerTests/StartupHookTests.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.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Xunit;

namespace Microsoft.Azure.Functions.Worker.Tests
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);
}
}
}
}