fix resource cleanup

This commit is contained in:
Vadim Lopatin 2014-05-05 13:28:22 +04:00
parent b553a7e1d0
commit 300bc90dbf
8 changed files with 135 additions and 73 deletions

View File

@ -41,7 +41,7 @@ import std.algorithm;
/// array based collection of items
/// retains item order when during add/remove operations
struct Collection(T) {
struct Collection(T, bool ownItems = false) {
private T[] _items;
private size_t _len;
/// returns true if there are no items in collection
@ -62,8 +62,11 @@ struct Collection(T) {
// shrink
static if (is(T == class) || is(T == struct)) {
// clear items
for (size_t i = newSize; i < _len; i++)
_items[i] = T.init;
for (size_t i = newSize; i < _len; i++) {
static if (ownItems)
destroy(_items[i]);
_items[i] = T.init;
}
}
} else if (newSize > _len) {
// expand
@ -130,7 +133,9 @@ struct Collection(T) {
size_t index = indexOf(value);
if (index == size_t.max)
return false;
remove(index);
T res = remove(index);
static if (ownItems)
destroy(res);
return true;
}
/// support of foreach with reference
@ -147,8 +152,11 @@ struct Collection(T) {
void clear() {
static if (is(T == class) || is(T == struct)) {
/// clear references
for(size_t i = 0; i < _len; i++)
_items[i] = T.init;
for(size_t i = 0; i < _len; i++) {
static if (ownItems)
destroy(_items[i]);
_items[i] = T.init;
}
}
_len = 0;
_items = null;

View File

@ -138,7 +138,16 @@ class DrawBuf : RefCountedObject {
version (USE_OPENGL) {
_id = drawBufIdGenerator++;
}
debug(resalloc) _instanceCount++;
}
debug(resalloc) private static int _instanceCount;
debug(resalloc) @property static int instanceCount() { return _instanceCount; }
~this() {
debug(resalloc) _instanceCount--;
clear();
}
protected void function(uint) _onDestroyCallback;
@property void onDestroyCallback(void function(uint) callback) { _onDestroyCallback = callback; }
@property void function(uint) onDestroyCallback() { return _onDestroyCallback; }
@ -355,7 +364,6 @@ class DrawBuf : RefCountedObject {
}
void clear() {}
~this() { clear(); }
}
alias DrawBufRef = Ref!DrawBuf;

View File

@ -5,6 +5,7 @@ import dlangui.graphics.fonts;
import derelict.freetype.ft;
private import dlangui.core.logger;
private import dlangui.core.collections;
private import std.algorithm;
private import std.file;
private import std.string;
@ -99,7 +100,7 @@ private class FreeTypeFontFile {
@property int weight() { return _weight; }
@property bool italic() { return _italic; }
//private static int _instanceCount;
debug private static int _instanceCount;
this(FT_Library library, string filename) {
_library = library;
_filename = filename;
@ -107,12 +108,12 @@ private class FreeTypeFontFile {
_matrix.yy = 0x10000;
_matrix.xy = 0;
_matrix.yx = 0;
//Log.d("Created FreeTypeFontFile, count=", ++_instanceCount);
debug Log.d("Created FreeTypeFontFile, count=", ++_instanceCount);
}
~this() {
clear();
//Log.d("Destroyed FreeTypeFontFile, count=", --_instanceCount);
debug Log.d("Destroyed FreeTypeFontFile, count=", --_instanceCount);
}
private static string familyName(FT_Face face)
@ -292,21 +293,21 @@ private class FreeTypeFontFile {
*/
class FreeTypeFont : Font {
private FontFileItem _fontItem;
private FreeTypeFontFile[] _files;
private Collection!(FreeTypeFontFile, true) _files;
static int _instanceCount;
debug(resalloc) static int _instanceCount;
/// need to call create() after construction to initialize font
this(FontFileItem item, int size) {
_fontItem = item;
_size = size;
_height = size;
debug Log.d("Created font, count=", ++_instanceCount);
debug(resalloc) Log.d("Created font, count=", ++_instanceCount);
}
/// do cleanup
~this() {
clear();
debug Log.d("Destroyed font, count=", --_instanceCount);
debug(resalloc) Log.d("Destroyed font, count=", --_instanceCount);
}
private int _size;
@ -317,10 +318,6 @@ class FreeTypeFont : Font {
/// cleanup resources
override void clear() {
foreach(ref FreeTypeFontFile file; _files) {
destroy(file);
file = null;
}
_files.clear();
}
@ -375,8 +372,10 @@ class FreeTypeFont : Font {
foreach (string filename; _fontItem.filenames) {
FreeTypeFontFile file = new FreeTypeFontFile(_fontItem.library, filename);
if (file.open(_size, 0)) {
_files ~= file;
}
_files.add(file);
} else {
destroy(file);
}
}
return _files.length > 0;
}
@ -503,6 +502,7 @@ class FreeTypeFontManager : FontManager {
weight = font.weight;
Log.d("Using properties from font file: face=", face, " weight=", weight, " italic=", italic);
}
destroy(font);
FontDef def = FontDef(family, face, italic, weight);
FontFileItem item = findFileItem(def);

View File

@ -24,8 +24,8 @@ Copyright: Vadim Lopatin, 2014
License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(WEB coolreader.org, Vadim Lopatin)
*/
module dlangui.graphics.resources;
module dlangui.graphics.resources;
import dlangui.graphics.images;
import dlangui.graphics.drawbuf;
import dlangui.core.logger;
@ -34,8 +34,8 @@ import std.algorithm;
import std.xml;
import std.algorithm;
import std.conv;
class Drawable : RefCountedObject {
//private static int _instanceCount;
this() {
@ -169,17 +169,28 @@ static Drawable createColorDrawable(string s) {
class ImageDrawable : Drawable {
protected DrawBufRef _image;
protected bool _tiled;
//private int _instanceCount;
debug(resalloc) private static int _instanceCount;
this(ref DrawBufRef image, bool tiled = false, bool ninePatch = false) {
_image = image;
_tiled = tiled;
if (ninePatch)
_image.detectNinePatch();
//Log.d("Created ImageDrawable, count=", ++_instanceCount);
debug(resalloc) {
_instanceCount++;
Log.d("Created ImageDrawable, count=", _instanceCount);
}
}
debug(resalloc) {
@property static int instanceCount() { return _instanceCount; }
}
~this() {
_image.clear();
//Log.d("Destroyed ImageDrawable, count=", --_instanceCount);
debug(resalloc) {
_instanceCount--;
Log.d("Destroyed ImageDrawable, count=", _instanceCount);
}
}
@property override int width() {
if (_image.isNull)
@ -328,29 +339,29 @@ void extractStateFlags(ref string[string] attr, ref uint stateMask, ref uint sta
sample:
(prefix android: is optional)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize=["true" | "false"]
android:dither=["true" | "false"]
android:variablePadding=["true" | "false"] >
<item
android:drawable="@[package:]drawable/drawable_resource"
android:state_pressed=["true" | "false"]
android:state_focused=["true" | "false"]
android:state_hovered=["true" | "false"]
android:state_selected=["true" | "false"]
android:state_checkable=["true" | "false"]
android:state_checked=["true" | "false"]
android:state_enabled=["true" | "false"]
android:state_activated=["true" | "false"]
android:state_window_focused=["true" | "false"] />
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize=["true" | "false"]
android:dither=["true" | "false"]
android:variablePadding=["true" | "false"] >
<item
android:drawable="@[package:]drawable/drawable_resource"
android:state_pressed=["true" | "false"]
android:state_focused=["true" | "false"]
android:state_hovered=["true" | "false"]
android:state_selected=["true" | "false"]
android:state_checkable=["true" | "false"]
android:state_checked=["true" | "false"]
android:state_enabled=["true" | "false"]
android:state_activated=["true" | "false"]
android:state_window_focused=["true" | "false"] />
</selector>
*/
*/
/// Drawable which is drawn depending on state (see http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList)
class StateDrawable : Drawable {
static struct StateItem {
static class StateItem {
uint stateMask;
uint stateValue;
ColorTransform transform;
@ -366,8 +377,14 @@ class StateDrawable : Drawable {
// max drawable size for all states
protected Point _size;
~this() {
foreach(ref item; _stateList)
destroy(item);
_stateList = null;
}
void addState(uint stateMask, uint stateValue, string resourceId, ref ColorTransform transform) {
StateItem item;
StateItem item = new StateItem();
item.stateMask = stateMask;
item.stateValue = stateValue;
item.drawable = drawableCache.get(resourceId, transform);
@ -375,14 +392,14 @@ class StateDrawable : Drawable {
}
void addState(uint stateMask, uint stateValue, DrawableRef drawable) {
StateItem item;
StateItem item = new StateItem();
item.stateMask = stateMask;
item.stateValue = stateValue;
item.drawable = drawable;
itemAdded(item);
}
private void itemAdded(ref StateItem item) {
private void itemAdded(StateItem item) {
_stateList ~= item;
if (!item.drawable.isNull) {
if (_size.x < item.drawable.width)
@ -471,12 +488,12 @@ class StateDrawable : Drawable {
import std.string;
try {
string s = cast(string)std.file.read(filename);
// Check for well-formedness
//check(s);
// Make a DOM tree
string s = cast(string)std.file.read(filename);
// Check for well-formedness
//check(s);
// Make a DOM tree
auto doc = new Document(s);
return load(doc);
@ -513,8 +530,8 @@ class StateDrawable : Drawable {
alias DrawableRef = Ref!Drawable;
/// decoded raster images cache (png, jpeg) -- access by filenames
class ImageCache {
@ -654,23 +671,29 @@ class DrawableCache {
DrawableRef _drawable;
DrawableRef[ColorTransform] _transformed;
//private int _instanceCount;
debug(resalloc) private static int _instanceCount;
this(string id, string filename, bool tiled) {
_id = id;
_filename = filename;
_tiled = tiled;
_error = filename is null;
//Log.d("Created DrawableCacheItem, count=", ++_instanceCount);
debug(resalloc) Log.d("Created DrawableCacheItem, count=", ++_instanceCount);
}
~this() {
_drawable.clear();
//Log.d("Destroyed DrawableCacheItem, count=", --_instanceCount);
foreach(ref t; _transformed)
t.clear();
_transformed.clear();
debug(resalloc) Log.d("Destroyed DrawableCacheItem, count=", --_instanceCount);
}
/// remove from memory, will cause reload on next access
void compact() {
if (!_drawable.isNull)
_drawable.clear();
}
foreach(t; _transformed)
t.clear();
_transformed.clear();
}
/// mark as not used
void checkpoint() {
_used = false;
@ -864,12 +887,15 @@ class DrawableCache {
debug Log.i("Creating DrawableCache");
}
~this() {
debug Log.i("Destroying DrawableCache");
debug(resalloc) Log.e("Drawable instace count before destroying of DrawableCache: ", ImageDrawable.instanceCount);
Log.i("Destroying DrawableCache _idToDrawableMap.length=", _idToDrawableMap.length);
foreach (ref item; _idToDrawableMap) {
destroy(item);
item = null;
}
_idToDrawableMap.clear();
debug(resalloc) Log.e("Drawable instace count after destroying of DrawableCache: ", ImageDrawable.instanceCount);
}
}

View File

@ -54,6 +54,8 @@ version(USE_SDL) {
}
if (_win)
SDL_DestroyWindow(_win);
if (_drawbuf)
destroy(_drawbuf);
}
version(USE_OPENGL) {
@ -798,7 +800,7 @@ version(USE_SDL) {
SDL_DisplayMode displayMode;
if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS) != 0) {
Log.e("Cannot init SDL2");
Log.e("Cannot init SDL2");
return 2;
}
scope(exit)SDL_Quit();
@ -828,7 +830,7 @@ version(USE_SDL) {
Platform.setInstance(null);
//
debug {
debug(resalloc) {
Widget.shuttingDown();
}
@ -836,10 +838,19 @@ version(USE_SDL) {
drawableCache = null;
imageCache = null;
FontManager.instance = null;
debug {
if (Widget.instanceCount() > 0) {
Log.e("Non-zero Widget instance count when exiting: ", Widget.instanceCount());
debug(resalloc) {
if (DrawBuf.instanceCount > 0) {
Log.e("Non-zero DrawBuf instance count when exiting: ", DrawBuf.instanceCount);
}
if (Style.instanceCount > 0) {
Log.e("Non-zero Style instance count when exiting: ", Style.instanceCount);
}
if (Widget.instanceCount() > 0) {
Log.e("Non-zero Widget instance count when exiting: ", Widget.instanceCount);
}
if (ImageDrawable.instanceCount > 0) {
Log.e("Non-zero ImageDrawable instance count when exiting: ", ImageDrawable.instanceCount);
}
}
Log.d("Exiting main");

View File

@ -123,6 +123,10 @@ class ImageWidget : Widget {
_drawableId = drawableId;
}
~this() {
_drawable.clear();
}
/// get drawable image id
@property string drawableId() { return _drawableId; }
/// set drawable image id

View File

@ -451,14 +451,18 @@ class Style {
return this;
}
private static int _instanceCount;
debug(resalloc) private static int _instanceCount;
debug(resalloc) @property static int instanceCount() { return _instanceCount; }
this(Theme theme, string id) {
_theme = theme;
_parentStyle = theme;
_id = id;
debug(resalloc) _instanceCount++;
//Log.d("Created style ", _id, ", count=", ++_instanceCount);
}
~this() {
foreach(ref Style item; _substates) {
//Log.d("Destroying substate");
@ -473,6 +477,7 @@ class Style {
_children.clear();
_backgroundDrawable.clear();
_font.clear();
debug(resalloc) _instanceCount--;
//Log.d("Destroyed style ", _id, ", parentId=", _parentId, ", state=", _stateMask, ", count=", --_instanceCount);
}

View File

@ -121,7 +121,7 @@ class Widget {
/// set new trackHover flag value (when true, widget will change Hover state while mouse is moving)
@property Widget trackHover(bool v) { _trackHover = v; return this; }
debug {
debug(resalloc) {
private static int _instanceCount = 0;
private static bool _appShuttingDown = false;
}
@ -129,13 +129,12 @@ class Widget {
this(string ID = null) {
_id = ID;
_state = State.Enabled;
debug {
_instanceCount++;
}
debug(resalloc) _instanceCount++;
//Log.d("Created widget, count = ", ++_instanceCount);
}
~this() {
debug {
debug(resalloc) {
//Log.v("destroying widget ", _id);
if (_appShuttingDown)
Log.e("Destroying widget ", _id, " after app shutdown: probably, resource leak");
@ -146,7 +145,8 @@ class Widget {
_ownStyle = null;
//Log.d("Destroyed widget, count = ", --_instanceCount);
}
debug {
debug(resalloc) {
/// 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.