Skip to content

Commit 86b2d8c

Browse files
merceyzamaitland
authored andcommitted
Improved dispose pattern in ChromiumWebBrowser (#2651)
* Code Cleanup - Improved ChromiumWebBrowser Dispose/Finalise * Added thread safe dispose pattern * Added IsDisposed * Wpf - Cleanup and improvements to the dispose pattern * WinForms - Cleanup and improvements to the dispose pattern * OffScreen - Cleanup and improvements to the dispose pattern * Added IsDisposed to IWebBrowser * Fixed comments * WinForms - Removed Finalizer and Dispose() * WPF - ChromiumWebBrowser.Dispose add comment for Interlocked.CompareExchange * WinForms - ChromiumWebBrowser.Dispose add comment for Interlocked.CompareExchange
1 parent d45c5c5 commit 86b2d8c

File tree

3 files changed

+178
-141
lines changed

3 files changed

+178
-141
lines changed

CefSharp.WinForms/ChromiumWebBrowser.cs

Lines changed: 65 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.ComponentModel;
77
using System.Drawing;
88
using System.Runtime.CompilerServices;
9+
using System.Threading;
910
using System.Windows.Forms;
1011
using CefSharp.Internals;
1112
using CefSharp.WinForms.Internals;
@@ -59,6 +60,25 @@ public class ChromiumWebBrowser : Control, IWebBrowserInternal, IWinFormsWebBrow
5960
/// </summary>
6061
private IRequestContext requestContext;
6162

63+
/// <summary>
64+
/// The value for disposal, if it's 1 (one) then this instance is either disposed
65+
/// or in the process of getting disposed
66+
/// </summary>
67+
private int disposeSignaled;
68+
69+
/// <summary>
70+
/// Gets a value indicating whether this instance is disposed.
71+
/// </summary>
72+
/// <value><see langword="true" /> if this instance is disposed; otherwise, <see langword="false" />.</value>
73+
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DefaultValue(false)]
74+
public new bool IsDisposed
75+
{
76+
get
77+
{
78+
return Interlocked.CompareExchange(ref disposeSignaled, 1, 1) == 1;
79+
}
80+
}
81+
6282
/// <summary>
6383
/// Set to true while handing an activating WM_ACTIVATE message.
6484
/// MUST ONLY be cleared by DefaultFocusHandler.
@@ -431,73 +451,71 @@ private void InitializeFieldsAndCefIfRequired()
431451
}
432452

433453
/// <summary>
434-
/// Releases the unmanaged resources used by the <see cref="T:System.Windows.Forms.Control" /> and its child controls and optionally releases the managed resources.
454+
/// If not in design mode; Releases unmanaged and - optionally - managed resources for the <see cref="ChromiumWebBrowser"/>
435455
/// </summary>
436-
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
456+
/// <param name="disposing"><see langword="true" /> to release both managed and unmanaged resources; <see langword="false" /> to release only unmanaged resources.</param>
437457
protected override void Dispose(bool disposing)
438458
{
439-
IsBrowserInitialized = false;
440-
441-
if (!designMode)
459+
// Attempt to move the disposeSignaled state from 0 to 1. If successful, we can be assured that
460+
// this thread is the first thread to do so, and can safely dispose of the object.
461+
if (Interlocked.CompareExchange(ref disposeSignaled, 1, 0) != 0)
442462
{
443-
RemoveFromListOfCefBrowsers();
463+
return;
444464
}
445465

446-
//The unmanaged resources should never be created in design mode, so only dispose when
447-
//at runtime
448-
if (disposing && !designMode)
466+
if (!designMode)
449467
{
450-
FreeUnmanagedResources();
468+
InternalDispose(disposing);
451469
}
452470

453-
// Don't maintain a reference to event listeners anylonger:
454-
AddressChanged = null;
455-
ConsoleMessage = null;
456-
FrameLoadEnd = null;
457-
FrameLoadStart = null;
458-
IsBrowserInitializedChanged = null;
459-
LoadError = null;
460-
LoadingStateChanged = null;
461-
StatusMessage = null;
462-
TitleChanged = null;
463-
464-
// Release reference to handlers, make sure this is done after we dispose managedCefBrowserAdapter
465-
// otherwise the ILifeSpanHandler.DoClose will not be invoked.
466-
this.SetHandlersToNull();
467-
468471
base.Dispose(disposing);
469472
}
470473

471474
/// <summary>
472-
/// Required for designer support - this method cannot be inlined as the designer
473-
/// will attempt to load libcef.dll and will subsiquently throw an exception.
475+
/// Releases unmanaged and - optionally - managed resources for the <see cref="ChromiumWebBrowser"/>
474476
/// </summary>
477+
/// <param name="disposing"><see langword="true" /> to release both managed and unmanaged resources; <see langword="false" /> to release only unmanaged resources.</param>
478+
/// <remarks>
479+
/// This method cannot be inlined as the designer will attempt to load libcef.dll and will subsiquently throw an exception.
480+
/// </remarks>
475481
[MethodImpl(MethodImplOptions.NoInlining)]
476-
private void RemoveFromListOfCefBrowsers()
482+
private void InternalDispose(bool disposing)
477483
{
478-
Cef.RemoveDisposable(this);
479-
}
484+
if (disposing)
485+
{
486+
IsBrowserInitialized = false;
480487

481-
/// <summary>
482-
/// Required for designer support - this method cannot be inlined as the designer
483-
/// will attempt to load libcef.dll and will subsiquently throw an exception.
484-
/// </summary>
485-
[MethodImpl(MethodImplOptions.NoInlining)]
486-
private void FreeUnmanagedResources()
487-
{
488-
browser = null;
488+
browser = null;
489489

490-
if (parentFormMessageInterceptor != null)
491-
{
492-
parentFormMessageInterceptor.Dispose();
493-
parentFormMessageInterceptor = null;
494-
}
490+
if (parentFormMessageInterceptor != null)
491+
{
492+
parentFormMessageInterceptor.Dispose();
493+
parentFormMessageInterceptor = null;
494+
}
495+
496+
if (managedCefBrowserAdapter != null)
497+
{
498+
managedCefBrowserAdapter.Dispose();
499+
managedCefBrowserAdapter = null;
500+
}
495501

496-
if (managedCefBrowserAdapter != null)
497-
{
498-
managedCefBrowserAdapter.Dispose();
499-
managedCefBrowserAdapter = null;
502+
// Don't maintain a reference to event listeners anylonger:
503+
AddressChanged = null;
504+
ConsoleMessage = null;
505+
FrameLoadEnd = null;
506+
FrameLoadStart = null;
507+
IsBrowserInitializedChanged = null;
508+
LoadError = null;
509+
LoadingStateChanged = null;
510+
StatusMessage = null;
511+
TitleChanged = null;
512+
513+
// Release reference to handlers, make sure this is done after we dispose managedCefBrowserAdapter
514+
// otherwise the ILifeSpanHandler.DoClose will not be invoked.
515+
this.SetHandlersToNull();
500516
}
517+
518+
Cef.RemoveDisposable(this);
501519
}
502520

503521
/// <summary>

0 commit comments

Comments
 (0)