Skip to content
This repository was archived by the owner on Apr 10, 2018. It is now read-only.

Commit bf8b9e7

Browse files
committed
Added a RequestGuard, which synchronizes requests from multiple threads. This fixes #19.
1 parent 5a838a5 commit bf8b9e7

6 files changed

Lines changed: 184 additions & 14 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
6+
using Xunit;
7+
8+
namespace RestSharp.Portable.Test
9+
{
10+
public class GuardTests
11+
{
12+
[Fact]
13+
public void TestGuardLock1()
14+
{
15+
var guard = new RequestGuard();
16+
var results = new ConcurrentBag<int>();
17+
var t1 = Task.Run(
18+
() =>
19+
{
20+
using (guard.Guard(CancellationToken.None))
21+
Thread.Sleep(TimeSpan.FromMilliseconds(500));
22+
results.Add(1);
23+
});
24+
Thread.Sleep(TimeSpan.FromMilliseconds(100));
25+
var t2 = Task.Run(
26+
() =>
27+
{
28+
using (guard.Guard(CancellationToken.None))
29+
DoNothing();
30+
results.Add(2);
31+
});
32+
Task.WaitAll(t1, t2);
33+
var resultsData = results.ToArray();
34+
Assert.Equal(1, resultsData[0]);
35+
Assert.Equal(2, resultsData[1]);
36+
}
37+
38+
[Fact]
39+
public void TestGuardLock2()
40+
{
41+
var guard = new RequestGuard();
42+
var results = new ConcurrentBag<int>();
43+
var t2 = Task.Run(
44+
() =>
45+
{
46+
Thread.Sleep(TimeSpan.FromMilliseconds(100));
47+
using (guard.Guard(CancellationToken.None))
48+
DoNothing();
49+
results.Add(2);
50+
});
51+
var t1 = Task.Run(
52+
() =>
53+
{
54+
using (guard.Guard(CancellationToken.None))
55+
Thread.Sleep(TimeSpan.FromMilliseconds(500));
56+
results.Add(1);
57+
});
58+
Task.WaitAll(t1, t2);
59+
var resultsData = results.ToArray();
60+
Assert.Equal(1, resultsData[0]);
61+
Assert.Equal(2, resultsData[1]);
62+
}
63+
64+
private static void DoNothing()
65+
{
66+
}
67+
}
68+
}

RestSharp.Portable.Test/IssueTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,33 @@ public void TestIssue16()
6464
}
6565
}
6666

67+
[Fact(DisplayName = "Issue 19")]
68+
public void TestIssue19()
69+
{
70+
using (var client = new RestClient("http://httpbin.org/"))
71+
{
72+
var req1 = new RestRequest("post", HttpMethod.Post);
73+
req1.AddParameter("a", "value-of-a");
74+
var t1 = client.Execute<PostResponse>(req1);
75+
76+
var req2 = new RestRequest("post", HttpMethod.Post);
77+
req2.AddParameter("ab", "value-of-ab");
78+
var t2 = client.Execute<PostResponse>(req2);
79+
80+
Task.WaitAll(t1, t2);
81+
82+
Assert.NotNull(t1.Result.Data);
83+
Assert.NotNull(t1.Result.Data.Form);
84+
Assert.True(t1.Result.Data.Form.ContainsKey("a"));
85+
Assert.Equal("value-of-a", t1.Result.Data.Form["a"]);
86+
87+
Assert.NotNull(t2.Result.Data);
88+
Assert.NotNull(t2.Result.Data.Form);
89+
Assert.True(t2.Result.Data.Form.ContainsKey("ab"));
90+
Assert.Equal("value-of-ab", t2.Result.Data.Form["ab"]);
91+
}
92+
}
93+
6794
// ReSharper disable once ClassNeverInstantiated.Local
6895
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local", Justification = "ReSharper bug")]
6996
private class PostResponse

RestSharp.Portable.Test/RestSharp.Portable.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
<Compile Include="AuthenticationTests.cs" />
7474
<Compile Include="CustomDeserializer.cs" />
7575
<Compile Include="DictionaryDeserializer.cs" />
76+
<Compile Include="GuardTests.cs" />
7677
<Compile Include="IssueTests.cs" />
7778
<Compile Include="OptionalHttpBasicAuthenticator.cs" />
7879
<Compile Include="OtherTests.cs" />

RestSharp.Portable/RequestGuard.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using System.Threading;
5+
6+
namespace RestSharp.Portable
7+
{
8+
internal class RequestGuard : IDisposable
9+
{
10+
private readonly object _syncObject = new object();
11+
12+
#if PCL || SILVERLIGHT
13+
private readonly AutoResetEvent _lock = new AutoResetEvent(true);
14+
#else
15+
private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
16+
#endif
17+
18+
private bool _isDisposed = false;
19+
20+
public IDisposable Guard(CancellationToken cancellationToken)
21+
{
22+
return new RequestLock(this, cancellationToken);
23+
}
24+
25+
public void Dispose()
26+
{
27+
lock (_syncObject)
28+
{
29+
if (_isDisposed)
30+
return;
31+
_lock.Dispose();
32+
_isDisposed = true;
33+
}
34+
}
35+
36+
private class RequestLock : IDisposable
37+
{
38+
private readonly RequestGuard _guard;
39+
40+
public RequestLock(RequestGuard guard, CancellationToken cancellationToken)
41+
{
42+
_guard = guard;
43+
#if PCL || SILVERLIGHT
44+
WaitHandle.WaitAny(new[] { guard._lock, cancellationToken.WaitHandle });
45+
cancellationToken.ThrowIfCancellationRequested();
46+
#else
47+
_guard._lock.Wait(cancellationToken);
48+
#endif
49+
}
50+
51+
public void Dispose()
52+
{
53+
#if PCL || SILVERLIGHT
54+
_guard._lock.Set();
55+
#else
56+
_guard._lock.Release();
57+
#endif
58+
}
59+
}
60+
}
61+
}

RestSharp.Portable/RestClient.cs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,17 @@ namespace RestSharp.Portable
1818
public class RestClient : IRestClient
1919
{
2020
private readonly IDictionary<string, IDeserializer> _contentHandlers = new Dictionary<string, IDeserializer>(StringComparer.OrdinalIgnoreCase);
21+
2122
private readonly IList<string> _acceptTypes = new List<string>();
2223

2324
private readonly IDictionary<string, IEncoding> _encodingHandlers = new Dictionary<string, IEncoding>(StringComparer.OrdinalIgnoreCase);
25+
2426
private readonly IList<string> _acceptEncodings = new List<string>();
2527

2628
private readonly List<Parameter> _defaultParameters = new List<Parameter>();
29+
30+
private readonly RequestGuard _requestGuard = new RequestGuard();
31+
2732
private HttpClient _httpClient;
2833

2934
/// <summary>
@@ -121,9 +126,9 @@ public IList<Parameter> DefaultParameters
121126
/// <returns>Response returned</returns>
122127
public async Task<IRestResponse> Execute(IRestRequest request)
123128
{
124-
using (var cts = new CancellationTokenSource())
129+
using (_requestGuard.Guard(CancellationToken.None))
125130
{
126-
using (var response = await ExecuteRequest(request, cts.Token))
131+
using (var response = await ExecuteRequest(request, CancellationToken.None))
127132
{
128133
var restResponse = new RestResponse(this, request);
129134
await restResponse.LoadResponse(response);
@@ -146,9 +151,9 @@ public async Task<IRestResponse> Execute(IRestRequest request)
146151
/// </returns>
147152
public async Task<IRestResponse<T>> Execute<T>(IRestRequest request)
148153
{
149-
using (var cts = new CancellationTokenSource())
154+
using (_requestGuard.Guard(CancellationToken.None))
150155
{
151-
using (var response = await ExecuteRequest(request, cts.Token))
156+
using (var response = await ExecuteRequest(request, CancellationToken.None))
152157
{
153158
var restResponse = new RestResponse<T>(this, request);
154159
await restResponse.LoadResponse(response);
@@ -165,11 +170,14 @@ public async Task<IRestResponse<T>> Execute<T>(IRestRequest request)
165170
/// <returns>Response returned</returns>
166171
public async Task<IRestResponse> Execute(IRestRequest request, CancellationToken ct)
167172
{
168-
using (var response = await ExecuteRequest(request, ct))
173+
using (_requestGuard.Guard(ct))
169174
{
170-
var restResponse = new RestResponse(this, request);
171-
await restResponse.LoadResponse(response);
172-
return restResponse;
175+
using (var response = await ExecuteRequest(request, ct))
176+
{
177+
var restResponse = new RestResponse(this, request);
178+
await restResponse.LoadResponse(response);
179+
return restResponse;
180+
}
173181
}
174182
}
175183

@@ -182,11 +190,14 @@ public async Task<IRestResponse> Execute(IRestRequest request, CancellationToken
182190
/// <returns>Response returned, with a deserialized object</returns>
183191
public async Task<IRestResponse<T>> Execute<T>(IRestRequest request, CancellationToken ct)
184192
{
185-
using (var response = await ExecuteRequest(request, ct))
193+
using (_requestGuard.Guard(ct))
186194
{
187-
var restResponse = new RestResponse<T>(this, request);
188-
await restResponse.LoadResponse(response);
189-
return restResponse;
195+
using (var response = await ExecuteRequest(request, ct))
196+
{
197+
var restResponse = new RestResponse<T>(this, request);
198+
await restResponse.LoadResponse(response);
199+
return restResponse;
200+
}
190201
}
191202
}
192203

@@ -348,6 +359,7 @@ public IEncoding GetEncoding(IEnumerable<string> encodingIds)
348359
/// </summary>
349360
public void Dispose()
350361
{
362+
_requestGuard.Dispose();
351363
if (_httpClient == null)
352364
return;
353365
_httpClient.Dispose();

RestSharp.Portable/RestSharp.Portable.projitems

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@
3131
<Compile Include="$(MSBuildThisFileDirectory)IRestResponse.cs" />
3232
<Compile Include="$(MSBuildThisFileDirectory)IRestResponseT.cs" />
3333
<Compile Include="$(MSBuildThisFileDirectory)JetBrains.Annotations.cs">
34-
<ExcludeFromStyleCop>true</ExcludeFromStyleCop>
35-
</Compile>
34+
<ExcludeFromStyleCop>true</ExcludeFromStyleCop>
35+
</Compile>
3636
<Compile Include="$(MSBuildThisFileDirectory)Parameter.cs" />
3737
<Compile Include="$(MSBuildThisFileDirectory)ParameterComparer.cs" />
3838
<Compile Include="$(MSBuildThisFileDirectory)ParameterExtensions.cs" />
3939
<Compile Include="$(MSBuildThisFileDirectory)ParameterType.cs" />
4040
<Compile Include="$(MSBuildThisFileDirectory)PostParametersContent.cs" />
4141
<Compile Include="$(MSBuildThisFileDirectory)PropertyFilterMode.cs" />
42+
<Compile Include="$(MSBuildThisFileDirectory)RequestGuard.cs" />
4243
<Compile Include="$(MSBuildThisFileDirectory)RestClient.cs" />
4344
<Compile Include="$(MSBuildThisFileDirectory)RestClientExtensions.cs" />
4445
<Compile Include="$(MSBuildThisFileDirectory)RestRequest.cs" />

0 commit comments

Comments
 (0)