cairo: Add mechanism to debug missing Dispose calls

Enabled by setting a new MONO_CAIRO_DEBUG_DISPOSE environment variable.
This commit is contained in:
Michael Hutchinson 2013-03-05 17:53:49 -05:00 committed by Bertrand Lorentz
parent 116d9fcc95
commit 5a78a5d177
7 changed files with 89 additions and 44 deletions

View file

@ -40,6 +40,49 @@ using Cairo;
namespace Cairo {
static class CairoDebug
{
static System.Collections.Generic.Dictionary<IntPtr,string> traces;
public static readonly bool Enabled;
static CairoDebug ()
{
var dbg = Environment.GetEnvironmentVariable ("MONO_CAIRO_DEBUG_DISPOSE");
if (dbg == null)
return;
Enabled = true;
traces = new System.Collections.Generic.Dictionary<IntPtr,string> ();
}
public static void OnAllocated (IntPtr obj)
{
if (!Enabled)
throw new InvalidOperationException ();
traces.Add (obj, Environment.StackTrace);
}
public static void OnDisposed<T> (IntPtr obj, bool disposing)
{
if (disposing && !Enabled)
throw new InvalidOperationException ();
if (!disposing) {
Console.Error.WriteLine ("{0} is leaking, programmer is missing a call to Dispose", typeof(T).FullName);
if (Enabled) {
Console.Error.WriteLine ("Allocated from:");
Console.Error.WriteLine (traces[obj]);
} else {
Console.Error.WriteLine ("Set MONO_CAIRO_DEBUG_DISPOSE to track allocation traces");
}
}
if (Enabled)
traces.Remove (obj);
}
}
public struct Point
{
public Point (int x, int y)
@ -204,12 +247,10 @@ namespace Cairo {
protected virtual void Dispose (bool disposing)
{
if (!disposing){
Console.Error.WriteLine ("Cairo.Context: called from finalization thread, programmer is missing a call to Dispose");
return;
}
if (state == IntPtr.Zero)
if (!disposing || CairoDebug.Enabled)
CairoDebug.OnDisposed<Context> (state, disposing);
if (!disposing|| state == IntPtr.Zero)
return;
//Console.WriteLine ("Destroying");

View file

@ -61,13 +61,11 @@ namespace Cairo
protected virtual void Dispose (bool disposing)
{
if (handle == IntPtr.Zero)
return;
if (!disposing || CairoDebug.Enabled)
CairoDebug.OnDisposed<FontFace> (handle, disposing);
if (!disposing) {
Console.Error.WriteLine ("Cairo.FontFace: called from finalization thread, programmer is missing a call to Dispose");
if (!disposing|| handle == IntPtr.Zero)
return;
}
NativeMethods.cairo_font_face_destroy (handle);
handle = IntPtr.Zero;
@ -77,6 +75,8 @@ namespace Cairo
public FontFace (IntPtr handle)
{
this.handle = handle;
if (CairoDebug.Enabled)
CairoDebug.OnAllocated (handle);
}
public IntPtr Handle {

View file

@ -47,6 +47,8 @@ namespace Cairo
internal FontOptions (IntPtr handle)
{
this.handle = handle;
if (CairoDebug.Enabled)
CairoDebug.OnAllocated (handle);
}
public FontOptions Copy ()
@ -68,13 +70,11 @@ namespace Cairo
protected virtual void Dispose (bool disposing)
{
if (handle == IntPtr.Zero)
return;
if (!disposing || CairoDebug.Enabled)
CairoDebug.OnDisposed<FontOptions> (handle, disposing);
if (!disposing) {
Console.Error.WriteLine ("Cairo.FontOptions: called from finalization thread, programmer is missing a call to Dispose");
if (!disposing|| handle == IntPtr.Zero)
return;
}
NativeMethods.cairo_font_options_destroy (handle);
handle = IntPtr.Zero;

View file

@ -41,6 +41,8 @@ namespace Cairo {
internal Path (IntPtr handle)
{
this.handle = handle;
if (CairoDebug.Enabled)
CairoDebug.OnAllocated (handle);
}
~Path ()
@ -57,12 +59,10 @@ namespace Cairo {
protected virtual void Dispose (bool disposing)
{
if (!disposing) {
Console.Error.WriteLine ("Cairo.Path: called from finalization thread, programmer is missing a call to Dispose");
return;
}
if (!disposing || CairoDebug.Enabled)
CairoDebug.OnDisposed<Path> (handle, disposing);
if (handle == IntPtr.Zero)
if (!disposing|| handle == IntPtr.Zero)
return;
NativeMethods.cairo_path_destroy (handle);

View file

@ -66,12 +66,15 @@ namespace Cairo {
static Hashtable patterns = new Hashtable ();
internal Pattern (IntPtr ptr)
internal Pattern (IntPtr handle)
{
lock (patterns){
patterns [ptr] = this;
patterns [handle] = this;
}
pattern = ptr;
Handle = handle;
if (CairoDebug.Enabled)
CairoDebug.OnAllocated (handle);
}
~Pattern ()
@ -98,16 +101,14 @@ namespace Cairo {
protected virtual void Dispose (bool disposing)
{
if (!disposing) {
Console.Error.WriteLine ("Cairo.Pattern: called from finalization thread, programmer is missing a call to Dispose");
return;
}
if (!disposing || CairoDebug.Enabled)
CairoDebug.OnDisposed<Pattern> (Handle, disposing);
if (pattern == IntPtr.Zero)
if (!disposing|| Handle == IntPtr.Zero)
return;
NativeMethods.cairo_pattern_destroy (pattern);
pattern = IntPtr.Zero;
NativeMethods.cairo_pattern_destroy (Handle);
Handle = IntPtr.Zero;
lock (patterns){
patterns.Remove (this);
}
@ -116,7 +117,7 @@ namespace Cairo {
[Obsolete ("Use Dispose()")]
public void Destroy ()
{
Dispose ();
NativeMethods.cairo_pattern_destroy (pattern);
}
public Extend Extend {
@ -143,6 +144,7 @@ namespace Cairo {
public IntPtr Handle {
get { return pattern; }
private set { pattern = value; }
}
[Obsolete ("Replaced by Handle property")]

View file

@ -35,11 +35,13 @@ namespace Cairo {
internal ScaledFont (IntPtr handle)
{
this.handle = handle;
if (CairoDebug.Enabled)
CairoDebug.OnAllocated (handle);
}
public ScaledFont (FontFace fontFace, Matrix matrix, Matrix ctm, FontOptions options)
: this (NativeMethods.cairo_scaled_font_create (fontFace.Handle, matrix, ctm, options.Handle))
{
handle = NativeMethods.cairo_scaled_font_create (fontFace.Handle, matrix, ctm, options.Handle);
}
~ScaledFont ()
@ -99,13 +101,11 @@ namespace Cairo {
protected virtual void Dispose (bool disposing)
{
if (handle == IntPtr.Zero)
return;
if (!disposing || CairoDebug.Enabled)
CairoDebug.OnDisposed<ScaledFont> (handle, disposing);
if (!disposing) {
Console.Error.WriteLine ("Cairo.ScaledFont: called from finalization thread, programmer is missing a call to Dispose");
if (!disposing|| handle == IntPtr.Zero)
return;
}
NativeMethods.cairo_scaled_font_destroy (handle);
handle = IntPtr.Zero;

View file

@ -43,6 +43,7 @@ namespace Cairo {
protected static Hashtable surfaces = new Hashtable ();
internal IntPtr surface = IntPtr.Zero;
[Obsolete]
protected Surface()
{
}
@ -55,6 +56,8 @@ namespace Cairo {
}
if (!owns)
NativeMethods.cairo_surface_reference (ptr);
if (CairoDebug.Enabled)
CairoDebug.OnAllocated (ptr);
}
static internal Surface LookupExternalSurface (IntPtr p)
@ -147,13 +150,12 @@ namespace Cairo {
protected virtual void Dispose (bool disposing)
{
if (surface == IntPtr.Zero)
return;
if (!disposing) {
Console.Error.WriteLine ("Cairo.Surface: called from finalization thread, programmer is missing a call to Dispose");
return;
}
if (!disposing || CairoDebug.Enabled)
CairoDebug.OnDisposed<Surface> (surface, disposing);
if (!disposing|| surface == IntPtr.Zero)
return;
lock (surfaces.SyncRoot)
surfaces.Remove (surface);