Skip to content

Commit

Permalink
[Mono.Cairo] Add mechanism to debug missing Dispose calls
Browse files Browse the repository at this point in the history
by setting a new MONO_CAIRO_DEBUG_DISPOSE env var
  • Loading branch information
mhutch committed Jun 19, 2013
1 parent abc2892 commit 0d1b8be
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 43 deletions.
55 changes: 49 additions & 6 deletions mcs/class/Mono.Cairo/Mono.Cairo/Context.cs
Expand Up @@ -41,6 +41,49 @@

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)
Expand Down Expand Up @@ -188,6 +231,8 @@ public Context (Surface surface)
public Context (IntPtr state)
{
this.state = state;
if (CairoDebug.Enabled)
CairoDebug.OnAllocated (state);
}

~Context ()
Expand All @@ -203,12 +248,10 @@ void IDisposable.Dispose ()

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");
Expand Down
10 changes: 5 additions & 5 deletions mcs/class/Mono.Cairo/Mono.Cairo/FontFace.cs
Expand Up @@ -61,13 +61,11 @@ public void Dispose ()

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;
Expand All @@ -77,6 +75,8 @@ protected virtual void Dispose (bool disposing)
public FontFace (IntPtr handle)
{
this.handle = handle;
if (CairoDebug.Enabled)
CairoDebug.OnAllocated (handle);
}

public IntPtr Handle {
Expand Down
10 changes: 5 additions & 5 deletions mcs/class/Mono.Cairo/Mono.Cairo/FontOptions.cs
Expand Up @@ -47,6 +47,8 @@ public FontOptions ()
internal FontOptions (IntPtr handle)
{
this.handle = handle;
if (CairoDebug.Enabled)
CairoDebug.OnAllocated (handle);
}

public FontOptions Copy ()
Expand All @@ -68,13 +70,11 @@ public void Dispose ()

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;
Expand Down
10 changes: 5 additions & 5 deletions mcs/class/Mono.Cairo/Mono.Cairo/Path.cs
Expand Up @@ -41,6 +41,8 @@ public class Path : IDisposable
internal Path (IntPtr handle)
{
this.handle = handle;
if (CairoDebug.Enabled)
CairoDebug.OnAllocated (handle);
}

~Path ()
Expand All @@ -57,12 +59,10 @@ public void Dispose ()

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);
Expand Down
25 changes: 15 additions & 10 deletions mcs/class/Mono.Cairo/Mono.Cairo/Pattern.cs
Expand Up @@ -66,12 +66,15 @@ protected Pattern ()

static Hashtable patterns = new Hashtable ();

internal Pattern (IntPtr ptr)
internal Pattern (IntPtr handle)
{
lock (patterns){
patterns [ptr] = this;
}
pattern = ptr;

Handle = handle;
if (CairoDebug.Enabled)
CairoDebug.OnAllocated (handle);
}

~Pattern ()
Expand All @@ -98,16 +101,14 @@ public void Dispose ()

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);
}
Expand All @@ -116,7 +117,7 @@ protected virtual void Dispose (bool disposing)
[Obsolete ("Use Dispose()")]
public void Destroy ()
{
Dispose ();
NativeMethods.cairo_pattern_destroy (pattern);
}

public Status Status
Expand All @@ -142,6 +143,10 @@ public Extend Extend
}
}

public IntPtr Handle {
get { return pattern; }
private set { pattern = value; }
}
public IntPtr Pointer {
get { return pattern; }
}
Expand Down
12 changes: 6 additions & 6 deletions mcs/class/Mono.Cairo/Mono.Cairo/ScaledFont.cs
Expand Up @@ -35,11 +35,13 @@ public class ScaledFont : IDisposable
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 ()
Expand Down Expand Up @@ -99,13 +101,11 @@ public void Dispose ()

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;
Expand Down
14 changes: 8 additions & 6 deletions mcs/class/Mono.Cairo/Mono.Cairo/Surface.cs
Expand Up @@ -43,6 +43,7 @@ public class Surface : IDisposable
protected static Hashtable surfaces = new Hashtable ();
internal IntPtr surface = IntPtr.Zero;

[Obsolete]
protected Surface()
{
}
Expand All @@ -55,6 +56,8 @@ protected Surface (IntPtr ptr, bool owns)
}
if (!owns)
NativeMethods.cairo_surface_reference (ptr);
if (CairoDebug.Enabled)
CairoDebug.OnAllocated (ptr);
}

static internal Surface LookupExternalSurface (IntPtr p)
Expand Down Expand Up @@ -147,13 +150,12 @@ public void Dispose ()

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);

Expand Down

0 comments on commit 0d1b8be

Please sign in to comment.