From 77d6014f585da22fbd53edfbdc7a3f7bcd12b38b Mon Sep 17 00:00:00 2001 From: Lokesh Gopu Date: Thu, 4 Jun 2026 14:08:05 -0400 Subject: [PATCH] Add thread-safety locks to StepsContext (#4475) --- src/Runner.Worker/StepsContext.cs | 74 +++++++++++++++++++------------ 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/src/Runner.Worker/StepsContext.cs b/src/Runner.Worker/StepsContext.cs index 6f16956e5..c7639970c 100644 --- a/src/Runner.Worker/StepsContext.cs +++ b/src/Runner.Worker/StepsContext.cs @@ -18,6 +18,7 @@ namespace GitHub.Runner.Worker { private static readonly Regex _propertyRegex = new("^[a-zA-Z_][a-zA-Z0-9_]*$", RegexOptions.Compiled); private readonly DictionaryContextData _contextData = new(); + private readonly object _lock = new(); /// /// Clears memory for a composite action's isolated "steps" context, after the action @@ -25,9 +26,12 @@ namespace GitHub.Runner.Worker /// public void ClearScope(string scopeName) { - if (_contextData.TryGetValue(scopeName, out _)) + lock (_lock) { - _contextData[scopeName] = new DictionaryContextData(); + if (_contextData.TryGetValue(scopeName, out _)) + { + _contextData[scopeName] = new DictionaryContextData(); + } } } @@ -41,23 +45,26 @@ namespace GitHub.Runner.Worker /// public DictionaryContextData GetScope(string scopeName) { - if (scopeName == null) + lock (_lock) { - scopeName = string.Empty; - } + if (scopeName == null) + { + scopeName = string.Empty; + } - var scope = default(DictionaryContextData); - if (_contextData.TryGetValue(scopeName, out var scopeValue)) - { - scope = scopeValue.AssertDictionary("scope"); - } - else - { - scope = new DictionaryContextData(); - _contextData.Add(scopeName, scope); - } + var scope = default(DictionaryContextData); + if (_contextData.TryGetValue(scopeName, out var scopeValue)) + { + scope = scopeValue.AssertDictionary("scope"); + } + else + { + scope = new DictionaryContextData(); + _contextData.Add(scopeName, scope); + } - return scope; + return scope; + } } public void SetOutput( @@ -67,16 +74,19 @@ namespace GitHub.Runner.Worker string value, out string reference) { - var step = GetStep(scopeName, stepName); - var outputs = step["outputs"].AssertDictionary("outputs"); - outputs[outputName] = new StringContextData(value); - if (_propertyRegex.IsMatch(outputName)) + lock (_lock) { - reference = $"steps.{stepName}.outputs.{outputName}"; - } - else - { - reference = $"steps['{stepName}']['outputs']['{outputName}']"; + var step = GetStep(scopeName, stepName); + var outputs = step["outputs"].AssertDictionary("outputs"); + outputs[outputName] = new StringContextData(value); + if (_propertyRegex.IsMatch(outputName)) + { + reference = $"steps.{stepName}.outputs.{outputName}"; + } + else + { + reference = $"steps['{stepName}']['outputs']['{outputName}']"; + } } } @@ -85,8 +95,11 @@ namespace GitHub.Runner.Worker string stepName, ActionResult conclusion) { - var step = GetStep(scopeName, stepName); - step["conclusion"] = new StringContextData(conclusion.ToString().ToLowerInvariant()); + lock (_lock) + { + var step = GetStep(scopeName, stepName); + step["conclusion"] = new StringContextData(conclusion.ToString().ToLowerInvariant()); + } } public void SetOutcome( @@ -94,8 +107,11 @@ namespace GitHub.Runner.Worker string stepName, ActionResult outcome) { - var step = GetStep(scopeName, stepName); - step["outcome"] = new StringContextData(outcome.ToString().ToLowerInvariant()); + lock (_lock) + { + var step = GetStep(scopeName, stepName); + step["outcome"] = new StringContextData(outcome.ToString().ToLowerInvariant()); + } } private DictionaryContextData GetStep(string scopeName, string stepName)