From 195a8587be03fc353dcc1d7513f307acee0aabd6 Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Fri, 13 Feb 2015 16:22:43 +0300 Subject: [PATCH] resource leak detection - always enable for debug builds --- src/dlangui/core/logger.d | 15 +++++++++++++ src/dlangui/graphics/drawbuf.d | 8 +++---- src/dlangui/graphics/ftfonts.d | 21 ++++++++++++------ src/dlangui/graphics/resources.d | 33 +++++++++++++++-------------- src/dlangui/platforms/sdl/sdlapp.d | 34 +++++++++++++++++++++--------- src/dlangui/widgets/combobox.d | 10 +++++++++ src/dlangui/widgets/styles.d | 8 +++---- src/dlangui/widgets/widget.d | 21 +++++++----------- 8 files changed, 96 insertions(+), 54 deletions(-) diff --git a/src/dlangui/core/logger.d b/src/dlangui/core/logger.d index 22f5dc86..fff11f6a 100644 --- a/src/dlangui/core/logger.d +++ b/src/dlangui/core/logger.d @@ -153,3 +153,18 @@ synchronized class Log { log(LogLevel.Fatal, args); } } + +debug { + private static __gshared bool _appShuttingDown = false; + + @property bool appShuttingDown() { return _appShuttingDown; } + + /// for debug purposes - sets shutdown flag to log widgets not destroyed in time. + void setAppShuttingDownFlag() { + _appShuttingDown = true; + } +} + +void onResourceDestroyWhileShutdown(string resourceName, string objname = null) { + Log.e("Resource leak: destroying resource while shutdown! ", resourceName, " ", objname); +} diff --git a/src/dlangui/graphics/drawbuf.d b/src/dlangui/graphics/drawbuf.d index 23565c5f..3fd046e2 100644 --- a/src/dlangui/graphics/drawbuf.d +++ b/src/dlangui/graphics/drawbuf.d @@ -81,13 +81,13 @@ class DrawBuf : RefCountedObject { version (USE_OPENGL) { _id = drawBufIdGenerator++; } - debug(resalloc) _instanceCount++; + debug _instanceCount++; } - debug(resalloc) private static int _instanceCount; - debug(resalloc) @property static int instanceCount() { return _instanceCount; } + debug private static __gshared int _instanceCount; + debug @property static int instanceCount() { return _instanceCount; } ~this() { - debug(resalloc) _instanceCount--; + debug _instanceCount--; clear(); } diff --git a/src/dlangui/graphics/ftfonts.d b/src/dlangui/graphics/ftfonts.d index e28a1de9..f5d1df1a 100644 --- a/src/dlangui/graphics/ftfonts.d +++ b/src/dlangui/graphics/ftfonts.d @@ -85,7 +85,7 @@ private class FontFileItem { } -private class FreeTypeFontFile { +class FreeTypeFontFile { private string _filename; private string _faceName; private FT_Library _library; @@ -112,6 +112,7 @@ private class FreeTypeFontFile { @property bool italic() { return _italic; } debug private static __gshared int _instanceCount; + debug @property static int instanceCount() { return _instanceCount; } this(FT_Library library, string filename) { _library = library; _filename = filename; @@ -119,12 +120,14 @@ private class FreeTypeFontFile { _matrix.yy = 0x10000; _matrix.xy = 0; _matrix.yx = 0; - debug(FontResources) Log.d("Created FreeTypeFontFile, count=", ++_instanceCount); + debug ++_instanceCount; + debug(FontResources) Log.d("Created FreeTypeFontFile, count=", _instanceCount); } ~this() { clear(); - debug(FontResources) Log.d("Destroyed FreeTypeFontFile, count=", --_instanceCount); + debug --_instanceCount; + debug(FontResources) Log.d("Destroyed FreeTypeFontFile, count=", _instanceCount); } private static string familyName(FT_Face face) @@ -329,25 +332,29 @@ private class FreeTypeFontFile { } /** -* Font implementation based on Win32 API system fonts. +* Font implementation based on FreeType. */ class FreeTypeFont : Font { private FontFileItem _fontItem; private Collection!(FreeTypeFontFile, true) _files; - debug(resalloc) static int _instanceCount; + debug static __gshared int _instanceCount; + debug @property static int instanceCount() { return _instanceCount; } + /// need to call create() after construction to initialize font this(FontFileItem item, int size) { _fontItem = item; _size = size; _height = size; - debug(resalloc) Log.d("Created font, count=", ++_instanceCount); + debug ++_instanceCount; + debug(resalloc) Log.d("Created font, count=", _instanceCount); } /// do cleanup ~this() { clear(); - debug(resalloc) Log.d("Destroyed font, count=", --_instanceCount); + debug --_instanceCount; + debug(resalloc) Log.d("Destroyed font, count=", _instanceCount); } private int _size; diff --git a/src/dlangui/graphics/resources.d b/src/dlangui/graphics/resources.d index 417d2e43..c5ec400a 100644 --- a/src/dlangui/graphics/resources.d +++ b/src/dlangui/graphics/resources.d @@ -211,12 +211,16 @@ immutable(ubyte[]) loadResourceBytes(string filename) { } class Drawable : RefCountedObject { - //private static int _instanceCount; + debug static __gshared int _instanceCount; + debug @property static int instanceCount() { return _instanceCount; } + this() { + debug ++_instanceCount; //Log.d("Created drawable, count=", ++_instanceCount); } ~this() { //Log.d("Destroyed drawable, count=", --_instanceCount); + debug --_instanceCount; } abstract void drawTo(DrawBuf buf, Rect rc, uint state = 0, int tilex0 = 0, int tiley0 = 0); @property abstract int width(); @@ -329,27 +333,21 @@ class ImageDrawable : Drawable { protected DrawBufRef _image; protected bool _tiled; - debug(resalloc) private static int _instanceCount; + debug static __gshared int _instanceCount; + debug @property static int instanceCount() { return _instanceCount; } this(ref DrawBufRef image, bool tiled = false, bool ninePatch = false) { _image = image; _tiled = tiled; if (ninePatch) _image.detectNinePatch(); - debug(resalloc) { - _instanceCount++; - Log.d("Created ImageDrawable, count=", _instanceCount); - } + debug _instanceCount++; + debug(resalloc) Log.d("Created ImageDrawable, count=", _instanceCount); } - debug(resalloc) { - @property static int instanceCount() { return _instanceCount; } - } ~this() { _image.clear(); - debug(resalloc) { - _instanceCount--; - Log.d("Destroyed ImageDrawable, count=", _instanceCount); - } + debug _instanceCount--; + debug(resalloc) Log.d("Destroyed ImageDrawable, count=", _instanceCount); } @property override int width() { if (_image.isNull) @@ -857,20 +855,23 @@ class DrawableCache { DrawableRef _drawable; DrawableRef[ColorTransform] _transformed; - debug(resalloc) private static int _instanceCount; + debug private static __gshared int _instanceCount; + debug @property static int instanceCount() { return _instanceCount; } this(string id, string filename, bool tiled) { _id = id; _filename = filename; _tiled = tiled; _error = filename is null; - debug(resalloc) Log.d("Created DrawableCacheItem, count=", ++_instanceCount); + debug ++_instanceCount; + debug(resalloc) Log.d("Created DrawableCacheItem, count=", _instanceCount); } ~this() { _drawable.clear(); foreach(ref t; _transformed) t.clear(); _transformed.destroy(); - debug(resalloc) Log.d("Destroyed DrawableCacheItem, count=", --_instanceCount); + debug --_instanceCount; + debug(resalloc) Log.d("Destroyed DrawableCacheItem, count=", _instanceCount); } /// remove from memory, will cause reload on next access void compact() { diff --git a/src/dlangui/platforms/sdl/sdlapp.d b/src/dlangui/platforms/sdl/sdlapp.d index fea4ad39..ce212540 100644 --- a/src/dlangui/platforms/sdl/sdlapp.d +++ b/src/dlangui/platforms/sdl/sdlapp.d @@ -873,6 +873,7 @@ class SDLPlatform : Platform { Log.i("entering message loop"); SDL_Event event; bool quit = false; + bool skipNextQuit = false; while(!quit) { //redrawWindows(); @@ -882,9 +883,12 @@ class SDLPlatform : Platform { //Log.d("Event.type = ", event.type); if (event.type == SDL_QUIT) { - Log.i("event.type == SDL_QUIT"); - quit = true; - break; + if (!skipNextQuit) { + Log.i("event.type == SDL_QUIT"); + quit = true; + break; + } + skipNextQuit = false; } if (_redrawEventId && event.type == _redrawEventId) { // user defined redraw event @@ -920,9 +924,13 @@ class SDLPlatform : Platform { w.redraw(); break; case SDL_WINDOWEVENT_CLOSE: - debug(DebugSDL) Log.d("SDL_WINDOWEVENT_CLOSE win=", event.window.windowID); - _windowMap.remove(windowID); - destroy(w); + if (w.handleCanClose()) { + debug(DebugSDL) Log.d("SDL_WINDOWEVENT_CLOSE win=", event.window.windowID); + _windowMap.remove(windowID); + destroy(w); + } else { + skipNextQuit = true; + } break; case SDL_WINDOWEVENT_SHOWN: debug(DebugSDL) Log.d("SDL_WINDOWEVENT_SHOWN"); @@ -1344,15 +1352,13 @@ int sdlmain(string[] args) { Platform.setInstance(null); // - debug(resalloc) { - Widget.shuttingDown(); - } + debug setAppShuttingDownFlag(); currentTheme = null; drawableCache = null; imageCache = null; FontManager.instance = null; - debug(resalloc) { + debug { if (DrawBuf.instanceCount > 0) { Log.e("Non-zero DrawBuf instance count when exiting: ", DrawBuf.instanceCount); } @@ -1365,6 +1371,14 @@ int sdlmain(string[] args) { if (ImageDrawable.instanceCount > 0) { Log.e("Non-zero ImageDrawable instance count when exiting: ", ImageDrawable.instanceCount); } + version (USE_FREETYPE) { + if (FreeTypeFontFile.instanceCount > 0) { + Log.e("Non-zero FreeTypeFontFile instance count when exiting: ", FreeTypeFontFile.instanceCount); + } + if (FreeTypeFont.instanceCount > 0) { + Log.e("Non-zero FreeTypeFont instance count when exiting: ", FreeTypeFont.instanceCount); + } + } } Log.d("Exiting main"); diff --git a/src/dlangui/widgets/combobox.d b/src/dlangui/widgets/combobox.d index 50677c46..c03c76a2 100644 --- a/src/dlangui/widgets/combobox.d +++ b/src/dlangui/widgets/combobox.d @@ -150,6 +150,9 @@ class ComboBoxBase : HorizontalLayout, OnClickHandler { addChild(_body); addChild(_button); } + + ~this() { + } } @@ -234,6 +237,13 @@ class ComboBox : ComboBoxBase { return res; } + ~this() { + if (_adapter) { + destroy(_adapter); + _adapter = null; + } + } + } /** Editable ComboBox with list of strings. */ diff --git a/src/dlangui/widgets/styles.d b/src/dlangui/widgets/styles.d index e054e92d..7cb3f394 100644 --- a/src/dlangui/widgets/styles.d +++ b/src/dlangui/widgets/styles.d @@ -699,14 +699,14 @@ class Style { return this; } - debug(resalloc) private static int _instanceCount; - debug(resalloc) @property static int instanceCount() { return _instanceCount; } + debug private static __gshared int _instanceCount; + debug @property static int instanceCount() { return _instanceCount; } this(Theme theme, string id) { _theme = theme; _parentStyle = theme; _id = id; - debug(resalloc) _instanceCount++; + debug _instanceCount++; //Log.d("Created style ", _id, ", count=", ++_instanceCount); } @@ -725,7 +725,7 @@ class Style { _children.destroy(); _backgroundDrawable.clear(); _font.clear(); - debug(resalloc) _instanceCount--; + debug _instanceCount--; //Log.d("Destroyed style ", _id, ", parentId=", _parentId, ", state=", _stateMask, ", count=", --_instanceCount); } diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index 3e1b1855..e1d953ef 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -174,9 +174,8 @@ class Widget { return CursorType.Arrow; } - debug(resalloc) { - private static int _instanceCount = 0; - private static bool _appShuttingDown = false; + debug { + private static __gshared int _instanceCount = 0; } /// empty parameter list constructor - for usage by factory this() { @@ -186,15 +185,15 @@ class Widget { this(string ID) { _id = ID; _state = State.Enabled; - debug(resalloc) _instanceCount++; + debug _instanceCount++; //Log.d("Created widget, count = ", ++_instanceCount); } ~this() { - debug(resalloc) { - //Log.v("destroying widget ", _id); - if (_appShuttingDown) - Log.e("Destroying widget ", _id, " after app shutdown: probably, resource leak"); + debug { + //Log.v("destroying widget ", _id, " ", this.classinfo.name); + if (appShuttingDown) + onResourceDestroyWhileShutdown(_id, this.classinfo.name); _instanceCount--; } if (_ownStyle !is null) @@ -203,13 +202,9 @@ class Widget { //Log.d("Destroyed widget, count = ", --_instanceCount); } - debug(resalloc) { + debug { /// for debug purposes - number of created widget objects, not yet destroyed static @property int instanceCount() { return _instanceCount; } - /// for debug purposes - sets shutdown flag to log widgets not destroyed in time. - static void shuttingDown() { - _appShuttingDown = true; - } } /// accessor to style - by lookup in theme by styleId (if style id is not set, theme base style will be used).