Skip to content

Commit 903e8ef

Browse files
authored
Merge ec98f0d into 1a3dc5b
2 parents 1a3dc5b + ec98f0d commit 903e8ef

15 files changed

+85
-32
lines changed

CefSharp.OffScreen/ChromiumWebBrowser.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ public Task<Bitmap> ScreenshotAsync(bool ignoreExistingScreenshot = false, Popup
507507
// Try our luck and see if there is already a screenshot, to save us creating a new thread for nothing.
508508
var screenshot = ScreenshotOrNull(blend);
509509

510-
var completionSource = new TaskCompletionSource<Bitmap>();
510+
var completionSource = new AsyncTaskCompletionSource<Bitmap>();
511511

512512
if (screenshot == null || ignoreExistingScreenshot)
513513
{

CefSharp.Test/Framework/AsyncExtensionFacts.cs

+14-11
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,25 @@ public async Task DeleteCookiesReturnsTaskWhenReturnsFalse()
3636
[Fact]
3737
public async Task TaskDeleteCookiesCallbackOnComplete()
3838
{
39-
const int numberOfCookiesDeleted = 10;
39+
for (var i = 0; i < 100; i++)
40+
{
41+
const int numberOfCookiesDeleted = 10;
4042

41-
var callback = new TaskDeleteCookiesCallback();
43+
var callback = new TaskDeleteCookiesCallback();
4244

43-
//Execute OnComplete on seperate Thread as in practice will be called on the CEF IO Thread.
44-
Task.Delay(100).ContinueWith(x =>
45-
{
46-
var c = (IDeleteCookiesCallback)callback;
45+
//Execute OnComplete on seperate Thread as in practice will be called on the CEF IO Thread.
46+
Task.Delay(100).ContinueWith(x =>
47+
{
48+
var c = (IDeleteCookiesCallback)callback;
4749

48-
c.OnComplete(numberOfCookiesDeleted);
49-
c.Dispose();
50-
}, TaskScheduler.Default);
50+
c.OnComplete(numberOfCookiesDeleted);
51+
c.Dispose();
52+
}, TaskScheduler.Default);
5153

52-
var result = await callback.Task;
54+
var result = await callback.Task;
5355

54-
Assert.Equal(numberOfCookiesDeleted, result);
56+
Assert.Equal(numberOfCookiesDeleted, result);
57+
}
5558
}
5659

5760
[Fact]

CefSharp/Callback/TaskCompletionCallback.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ namespace CefSharp
1010
{
1111
public class TaskCompletionCallback : ICompletionCallback
1212
{
13-
private readonly TaskCompletionSource<bool> taskCompletionSource;
13+
private readonly AsyncTaskCompletionSource<bool> taskCompletionSource;
1414

1515
public TaskCompletionCallback()
1616
{
17-
taskCompletionSource = new TaskCompletionSource<bool>();
17+
taskCompletionSource = new AsyncTaskCompletionSource<bool>();
1818
}
1919

2020
void ICompletionCallback.OnComplete()

CefSharp/Callback/TaskDeleteCookiesCallback.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ public class TaskDeleteCookiesCallback : IDeleteCookiesCallback
1515
{
1616
public const int InvalidNoOfCookiesDeleted = -1;
1717

18-
private readonly TaskCompletionSource<int> taskCompletionSource;
18+
private readonly AsyncTaskCompletionSource<int> taskCompletionSource;
1919
private volatile bool isDisposed;
2020

2121
public TaskDeleteCookiesCallback()
2222
{
23-
taskCompletionSource = new TaskCompletionSource<int>();
23+
taskCompletionSource = new AsyncTaskCompletionSource<int>();
2424
}
2525

2626
void IDeleteCookiesCallback.OnComplete(int numDeleted)

CefSharp/Callback/TaskPrintToPdfCallback.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace CefSharp
1010
{
1111
public sealed class TaskPrintToPdfCallback : IPrintToPdfCallback
1212
{
13-
private readonly TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
13+
private readonly AsyncTaskCompletionSource<bool> taskCompletionSource = new AsyncTaskCompletionSource<bool>();
1414

1515
public Task<bool> Task
1616
{

CefSharp/Callback/TaskRegisterCdmCallback.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ namespace CefSharp
1313
/// </summary>
1414
public class TaskRegisterCdmCallback: IRegisterCdmCallback
1515
{
16-
private readonly TaskCompletionSource<CdmRegistration> taskCompletionSource;
16+
private readonly AsyncTaskCompletionSource<CdmRegistration> taskCompletionSource;
1717

1818
public TaskRegisterCdmCallback()
1919
{
20-
taskCompletionSource = new TaskCompletionSource<CdmRegistration>();
20+
taskCompletionSource = new AsyncTaskCompletionSource<CdmRegistration>();
2121
}
2222

2323
void IRegisterCdmCallback.OnRegistrationComplete(CdmRegistration registration)

CefSharp/Callback/TaskResolveCallback.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ namespace CefSharp
1010
{
1111
public class TaskResolveCallback : IResolveCallback
1212
{
13-
private readonly TaskCompletionSource<ResolveCallbackResult> taskCompletionSource;
13+
private readonly AsyncTaskCompletionSource<ResolveCallbackResult> taskCompletionSource;
1414

1515
public TaskResolveCallback()
1616
{
17-
taskCompletionSource = new TaskCompletionSource<ResolveCallbackResult>();
17+
taskCompletionSource = new AsyncTaskCompletionSource<ResolveCallbackResult>();
1818
}
1919

2020
public void OnResolveCompleted(CefErrorCode result, IList<string> resolvedIpAddresses)

CefSharp/Callback/TaskSetCookieCallback.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ namespace CefSharp
1313
/// </summary>
1414
public class TaskSetCookieCallback : ISetCookieCallback
1515
{
16-
private readonly TaskCompletionSource<bool> taskCompletionSource;
16+
private readonly AsyncTaskCompletionSource<bool> taskCompletionSource;
1717
private volatile bool isDisposed;
1818

1919
public TaskSetCookieCallback()
2020
{
21-
taskCompletionSource = new TaskCompletionSource<bool>();
21+
taskCompletionSource = new AsyncTaskCompletionSource<bool>();
2222
}
2323

2424
void ISetCookieCallback.OnComplete(bool success)

CefSharp/CefSharp.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
<ItemGroup>
7373
<Compile Include="AsyncExtensions.cs" />
7474
<Compile Include="BindingOptions.cs" />
75+
<Compile Include="Internals\AsyncTaskCompletionSource.cs" />
7576
<Compile Include="Callback\IAuthCallback.cs" />
7677
<Compile Include="Callback\IBeforeDownloadCallback.cs" />
7778
<Compile Include="Callback\ICallback.cs" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright © 2010-2017 The CefSharp Authors. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4+
5+
using System;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
9+
namespace CefSharp
10+
{
11+
/// <summary>
12+
/// Wraps <see cref="TaskCompletionSource{T}"/> by providing a way to set its result in an asynchronous
13+
/// fashion. This prevents the resulting Task continuations from being executed synchronously on the same
14+
/// thread as the one completing the Task, which may be better when that thread is a CEF UI thread.
15+
/// </summary>
16+
/// <typeparam name="T"></typeparam>
17+
public class AsyncTaskCompletionSource<T>
18+
{
19+
private readonly TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
20+
private int resultSet = 0;
21+
22+
/// <summary>
23+
/// Gets the <see cref="Task{TResult}"/> created by the wrapped <see cref="AsyncTaskCompletionSource{T}"/> instance.
24+
/// </summary>
25+
/// <seealso cref="TaskCompletionSource{TResult}.Task"/>
26+
public Task<T> Task => tcs.Task;
27+
28+
/// <summary>
29+
/// Sets the wrapped <see cref="TaskCompletionSource{T}"/> instance result in an asynchronous fashion.
30+
/// This prevents the Task continuations from being executed synchronously on the same thread as the
31+
/// one calling this method, which is required when this thread is a CEF UI thread.
32+
/// </summary>
33+
/// <param name="result">result</param>
34+
public void TrySetResultAsync(T result)
35+
{
36+
if (Interlocked.Exchange(ref resultSet, 1) == 0)
37+
{
38+
System.Threading.Tasks.Task.Factory.StartNew(() => tcs.TrySetResult(result),
39+
CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
40+
}
41+
}
42+
}
43+
}

CefSharp/Internals/TaskExtensions.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,14 @@ public static TaskCompletionSource<TResult> WithTimeout<TResult>(this TaskComple
8989
}
9090

9191
/// <summary>
92+
/// <para>
9293
/// Set the TaskCompletionSource in an async fashion. This prevents the Task Continuation being executed sync on the same thread
93-
/// This is required otherwise contintinuations will happen on CEF UI threads
94+
/// This is required otherwise contintinuations will happen on CEF UI threads.
95+
/// </para>
96+
/// <para>
97+
/// USAGE WARNING: if this method is called twice on the same task completion source, the result it not deterministic.
98+
/// Use it with care, or use <see cref="AsyncTaskCompletionSource{T}"/>.
99+
/// </para>
94100
/// </summary>
95101
/// <typeparam name="TResult">Generic param</typeparam>
96102
/// <param name="taskCompletionSource">tcs</param>

CefSharp/Visitor/TaskCookieVisitor.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ namespace CefSharp
1515
/// </summary>
1616
public class TaskCookieVisitor : ICookieVisitor
1717
{
18-
private readonly TaskCompletionSource<List<Cookie>> taskCompletionSource;
18+
private readonly AsyncTaskCompletionSource<List<Cookie>> taskCompletionSource;
1919
private List<Cookie> list;
2020

2121
/// <summary>
2222
/// Default constructor
2323
/// </summary>
2424
public TaskCookieVisitor()
2525
{
26-
taskCompletionSource = new TaskCompletionSource<List<Cookie>>();
26+
taskCompletionSource = new AsyncTaskCompletionSource<List<Cookie>>();
2727
list = new List<Cookie>();
2828
}
2929

CefSharp/Visitor/TaskNavigationEntryVisitor.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ namespace CefSharp
1515
/// </summary>
1616
public class TaskNavigationEntryVisitor : INavigationEntryVisitor
1717
{
18-
private TaskCompletionSource<List<NavigationEntry>> taskCompletionSource;
18+
private AsyncTaskCompletionSource<List<NavigationEntry>> taskCompletionSource;
1919
private List<NavigationEntry> list;
2020

2121
/// <summary>
2222
/// Default constructor
2323
/// </summary>
2424
public TaskNavigationEntryVisitor()
2525
{
26-
taskCompletionSource = new TaskCompletionSource<List<NavigationEntry>>();
26+
taskCompletionSource = new AsyncTaskCompletionSource<List<NavigationEntry>>();
2727
list = new List<NavigationEntry>();
2828
}
2929

CefSharp/Visitor/TaskStringVisitor.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ namespace CefSharp
1414
/// </summary>
1515
public class TaskStringVisitor : IStringVisitor
1616
{
17-
private readonly TaskCompletionSource<string> taskCompletionSource;
17+
private readonly AsyncTaskCompletionSource<string> taskCompletionSource;
1818

1919
/// <summary>
2020
/// Default constructor
2121
/// </summary>
2222
public TaskStringVisitor()
2323
{
24-
taskCompletionSource = new TaskCompletionSource<string>();
24+
taskCompletionSource = new AsyncTaskCompletionSource<string>();
2525
}
2626

2727
/// <summary>

CefSharp/Visitor/TaskWebPluginInfoVisitor.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ namespace CefSharp
1111
{
1212
public class TaskWebPluginInfoVisitor : IWebPluginInfoVisitor
1313
{
14-
private TaskCompletionSource<List<WebPluginInfo>> taskCompletionSource;
14+
private AsyncTaskCompletionSource<List<WebPluginInfo>> taskCompletionSource;
1515
private List<WebPluginInfo> list;
1616

1717
public TaskWebPluginInfoVisitor()
1818
{
19-
taskCompletionSource = new TaskCompletionSource<List<WebPluginInfo>>();
19+
taskCompletionSource = new AsyncTaskCompletionSource<List<WebPluginInfo>>();
2020
list = new List<WebPluginInfo>();
2121
}
2222

0 commit comments

Comments
 (0)