From 034ec06aeca262b414ab313c9488cc47bcb75fd1 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 22 Aug 2023 13:09:57 -0400 Subject: [PATCH] Use optimistic synchronization in JsonWebToken.Audiences (#2243) I didn't see anything in Payload.TryGetValue that would require synchronization with a concurrent call to Payload.TryGetValue. And without that, and since concurrent access is not something to optimize for here, we can instead employ optimistic synchronization and just swap in the result with an interlocked. If there's a race condition and two threads do end up accessing Audiences on the same instance concurrently, they may both end up creating the array, but only one will be published for all to consume. In addition to making Audiences cheaper to access, the primary benefit here is it saves an allocation per JsonWebToken, which no longer needs to construct the audiences lock object. --- .../JsonWebToken.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs index 6e37ecfcab..06cfe0cbba 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Security.Claims; using System.Text; +using System.Threading; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; @@ -17,7 +18,6 @@ namespace Microsoft.IdentityModel.JsonWebTokens /// public class JsonWebToken : SecurityToken { - internal object _audiencesLock = new(); private ClaimsIdentity _claimsIdentity; private bool _wasClaimsIdentitySet; @@ -614,12 +614,11 @@ public IEnumerable Audiences { if (_audiences == null) { - lock (_audiencesLock) - { - _audiences ??= Payload.TryGetValue(JwtRegisteredClaimNames.Aud, out IList audiences) ? - (audiences is string[] audiencesArray ? audiencesArray : audiences.ToArray()) : - Array.Empty(); - } + var tmp = Payload.TryGetValue(JwtRegisteredClaimNames.Aud, out IList audiences) ? + (audiences is string[] audiencesArray ? audiencesArray : audiences.ToArray()) : + Array.Empty(); + + Interlocked.CompareExchange(ref _audiences, tmp, null); } return _audiences;