Skip to content

Commit 3246f0b

Browse files
authored
Merge pull request Azure#2864 from markcowl/concurrency3
Fix for Azure#2701, guarding shared complex collections with reader/writer…
2 parents 5cdc1c8 + 8d03c14 commit 3246f0b

File tree

1 file changed

+86
-24
lines changed

1 file changed

+86
-24
lines changed

src/Common/Commands.Common.Authentication/Factories/ClientFactory.cs

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616
using Microsoft.Azure.Commands.Common.Authentication.Models;
1717
using Microsoft.Azure.Commands.Common.Authentication.Properties;
1818
using System;
19+
using System.Collections.Concurrent;
1920
using System.Collections.Generic;
2021
using System.Collections.Specialized;
22+
using System.Linq;
2123
using System.Net;
2224
using System.Net.Http;
2325
using System.Net.Http.Headers;
26+
using System.Threading;
2427

2528
namespace Microsoft.Azure.Commands.Common.Authentication.Factories
2629
{
@@ -30,12 +33,16 @@ public class ClientFactory : IClientFactory
3033

3134
private Dictionary<Type, IClientAction> _actions;
3235
private OrderedDictionary _handlers;
36+
private ReaderWriterLockSlim _actionsLock;
37+
private ReaderWriterLockSlim _handlersLock;
3338

3439
public ClientFactory()
3540
{
3641
_actions = new Dictionary<Type, IClientAction>();
42+
_actionsLock = new ReaderWriterLockSlim();
3743
UserAgents = new HashSet<ProductInfoHeaderValue>();
3844
_handlers = new OrderedDictionary();
45+
_handlersLock = new ReaderWriterLockSlim();
3946
}
4047

4148
public virtual TClient CreateArmClient<TClient>(AzureContext context, AzureEnvironment.Endpoint endpoint) where TClient : Microsoft.Rest.ServiceClient<TClient>
@@ -108,8 +115,7 @@ public virtual TClient CreateClient<TClient>(AzureContext context, AzureEnvironm
108115
public virtual TClient CreateClient<TClient>(AzureSMProfile profile, AzureEnvironment.Endpoint endpoint) where TClient : ServiceClient<TClient>
109116
{
110117
TClient client = CreateClient<TClient>(profile.Context, endpoint);
111-
112-
foreach (IClientAction action in _actions.Values)
118+
foreach (IClientAction action in GetActions())
113119
{
114120
action.Apply<TClient>(client, profile, endpoint);
115121
}
@@ -154,7 +160,7 @@ public virtual TClient CreateClient<TClient>(AzureSMProfile profile, AzureSubscr
154160

155161
TClient client = CreateClient<TClient>(context, endpoint);
156162

157-
foreach (IClientAction action in _actions.Values)
163+
foreach (IClientAction action in GetActions())
158164
{
159165
action.Apply<TClient>(client, profile, endpoint);
160166
}
@@ -242,34 +248,82 @@ public static HttpClientHandler CreateHttpClientHandler(string endpoint, ICreden
242248

243249
public void AddAction(IClientAction action)
244250
{
245-
if (action != null)
251+
_actionsLock.EnterWriteLock();
252+
try
253+
{
254+
if (action != null)
255+
{
256+
action.ClientFactory = this;
257+
_actions[action.GetType()] = action;
258+
}
259+
}
260+
finally
246261
{
247-
action.ClientFactory = this;
248-
_actions[action.GetType()] = action;
262+
_actionsLock.ExitWriteLock();
249263
}
250264
}
251265

252266
public void RemoveAction(Type actionType)
253267
{
254-
if (_actions.ContainsKey(actionType))
268+
_actionsLock.EnterWriteLock();
269+
try
255270
{
256-
_actions.Remove(actionType);
271+
if (_actions.ContainsKey(actionType))
272+
{
273+
_actions.Remove(actionType);
274+
}
275+
}
276+
finally
277+
{
278+
_actionsLock.ExitWriteLock();
257279
}
258280
}
259281

282+
private IClientAction[] GetActions()
283+
{
284+
IClientAction[] result = null;
285+
_actionsLock.EnterReadLock();
286+
try
287+
{
288+
result = _actions.Values.ToArray();
289+
}
290+
finally
291+
{
292+
_actionsLock.ExitReadLock();
293+
}
294+
295+
return result;
296+
}
297+
260298
public void AddHandler<T>(T handler) where T : DelegatingHandler, ICloneable
261299
{
262-
if (handler != null)
300+
_handlersLock.EnterWriteLock();
301+
try
263302
{
264-
_handlers[handler.GetType()] = handler;
303+
if (handler != null)
304+
{
305+
_handlers[handler.GetType()] = handler;
306+
}
307+
}
308+
finally
309+
{
310+
_handlersLock.ExitWriteLock();
265311
}
266312
}
267313

268314
public void RemoveHandler(Type handlerType)
269315
{
270-
if (_handlers.Contains(handlerType))
316+
_handlersLock.EnterWriteLock();
317+
try
318+
{
319+
if (_handlers.Contains(handlerType))
320+
{
321+
_handlers.Remove(handlerType);
322+
}
323+
}
324+
finally
271325
{
272-
_handlers.Remove(handlerType);
326+
_handlersLock.ExitWriteLock();
273327
}
274328
}
275329

@@ -296,24 +350,32 @@ public void AddUserAgent(string productName)
296350

297351
public DelegatingHandler[] GetCustomHandlers()
298352
{
299-
List<DelegatingHandler> newHandlers = new List<DelegatingHandler>();
300-
var enumerator = _handlers.GetEnumerator();
301-
while (enumerator.MoveNext())
353+
_handlersLock.EnterReadLock();
354+
try
302355
{
303-
var handler = enumerator.Value;
304-
ICloneable cloneableHandler = handler as ICloneable;
305-
if (cloneableHandler != null)
356+
List<DelegatingHandler> newHandlers = new List<DelegatingHandler>();
357+
var enumerator = _handlers.GetEnumerator();
358+
while (enumerator.MoveNext())
306359
{
307-
var newHandler = cloneableHandler.Clone();
308-
DelegatingHandler convertedHandler = newHandler as DelegatingHandler;
309-
if (convertedHandler != null)
360+
var handler = enumerator.Value;
361+
ICloneable cloneableHandler = handler as ICloneable;
362+
if (cloneableHandler != null)
310363
{
311-
newHandlers.Add(convertedHandler);
364+
var newHandler = cloneableHandler.Clone();
365+
DelegatingHandler convertedHandler = newHandler as DelegatingHandler;
366+
if (convertedHandler != null)
367+
{
368+
newHandlers.Add(convertedHandler);
369+
}
312370
}
313371
}
314-
}
315372

316-
return newHandlers.ToArray();
373+
return newHandlers.ToArray();
374+
}
375+
finally
376+
{
377+
_handlersLock.ExitReadLock();
378+
}
317379
}
318380
}
319381
}

0 commit comments

Comments
 (0)