Fix a memory leak in SkGPipeCanvas.
In cross process mode (currently unused but tested on the bots), SkGPipeCanvas has a tangle of circular references, which prevents it from being deleted. Break the chain of references. R=junov@chromium.org Review URL: https://codereview.chromium.org/69633003 git-svn-id: http://skia.googlecode.com/svn/trunk@12237 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
d6bea606c6
commit
59c3ab637b
@ -167,32 +167,63 @@ public:
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If SkBitmaps are to be flattened to send to the reader, this class is
|
||||||
|
* provided to the SkBitmapHeap to tell the SkGPipeCanvas to do so.
|
||||||
|
*/
|
||||||
|
class BitmapShuttle : public SkBitmapHeap::ExternalStorage {
|
||||||
|
public:
|
||||||
|
BitmapShuttle(SkGPipeCanvas*);
|
||||||
|
|
||||||
|
~BitmapShuttle();
|
||||||
|
|
||||||
|
virtual bool insert(const SkBitmap& bitmap, int32_t slot) SK_OVERRIDE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the SkGPipeCanvas used for insertion. After this, calls to
|
||||||
|
* insert will crash.
|
||||||
|
*/
|
||||||
|
void removeCanvas();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SkGPipeCanvas* fCanvas;
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class SkGPipeCanvas : public SkCanvas {
|
class SkGPipeCanvas : public SkCanvas {
|
||||||
public:
|
public:
|
||||||
SkGPipeCanvas(SkGPipeController*, SkWriter32*, uint32_t flags,
|
SkGPipeCanvas(SkGPipeController*, SkWriter32*, uint32_t flags,
|
||||||
uint32_t width, uint32_t height);
|
uint32_t width, uint32_t height);
|
||||||
virtual ~SkGPipeCanvas();
|
virtual ~SkGPipeCanvas();
|
||||||
|
|
||||||
void finish() {
|
/**
|
||||||
if (!fDone) {
|
* Called when nothing else is to be written to the stream. Any repeated
|
||||||
if (this->needOpBytes()) {
|
* calls are ignored.
|
||||||
this->writeOp(kDone_DrawOp);
|
*
|
||||||
this->doNotify();
|
* @param notifyReaders Whether to send a message to the reader(s) that
|
||||||
if (shouldFlattenBitmaps(fFlags)) {
|
* the writer is through sending commands. Should generally be true,
|
||||||
// In this case, a BitmapShuttle is reffed by the SkBitmapHeap
|
* unless there is an error which prevents further messages from
|
||||||
// and refs this canvas. Unref the SkBitmapHeap to end the
|
* being sent.
|
||||||
// circular reference. When shouldFlattenBitmaps is false,
|
*/
|
||||||
// there is no circular reference, so the SkBitmapHeap can be
|
void finish(bool notifyReaders) {
|
||||||
// safely unreffed in the destructor.
|
if (fDone) {
|
||||||
fBitmapHeap->unref();
|
return;
|
||||||
// This eliminates a similar circular reference (Canvas owns
|
|
||||||
// the FlattenableHeap which holds a ref to the SkBitmapHeap).
|
|
||||||
fFlattenableHeap.setBitmapStorage(NULL);
|
|
||||||
fBitmapHeap = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fDone = true;
|
|
||||||
}
|
}
|
||||||
|
if (notifyReaders && this->needOpBytes()) {
|
||||||
|
this->writeOp(kDone_DrawOp);
|
||||||
|
this->doNotify();
|
||||||
|
}
|
||||||
|
if (shouldFlattenBitmaps(fFlags)) {
|
||||||
|
// The following circular references exist:
|
||||||
|
// fFlattenableHeap -> fWriteBuffer -> fBitmapStorage -> fExternalStorage -> fCanvas
|
||||||
|
// fBitmapHeap -> fExternalStorage -> fCanvas
|
||||||
|
// fFlattenableHeap -> fBitmapStorage -> fExternalStorage -> fCanvas
|
||||||
|
|
||||||
|
// Break them all by destroying the final link to this SkGPipeCanvas.
|
||||||
|
fBitmapShuttle->removeCanvas();
|
||||||
|
}
|
||||||
|
fDone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void flushRecording(bool detachCurrentBlock);
|
void flushRecording(bool detachCurrentBlock);
|
||||||
@ -306,9 +337,11 @@ private:
|
|||||||
// if a new SkFlatData was added when in cross process mode
|
// if a new SkFlatData was added when in cross process mode
|
||||||
void flattenFactoryNames();
|
void flattenFactoryNames();
|
||||||
|
|
||||||
FlattenableHeap fFlattenableHeap;
|
FlattenableHeap fFlattenableHeap;
|
||||||
FlatDictionary fFlatDictionary;
|
FlatDictionary fFlatDictionary;
|
||||||
int fCurrFlatIndex[kCount_PaintFlats];
|
SkAutoTUnref<BitmapShuttle> fBitmapShuttle;
|
||||||
|
int fCurrFlatIndex[kCount_PaintFlats];
|
||||||
|
|
||||||
int flattenToIndex(SkFlattenable* obj, PaintFlats);
|
int flattenToIndex(SkFlattenable* obj, PaintFlats);
|
||||||
|
|
||||||
// Common code used by drawBitmap*. Behaves differently depending on the
|
// Common code used by drawBitmap*. Behaves differently depending on the
|
||||||
@ -390,24 +423,6 @@ int SkGPipeCanvas::flattenToIndex(SkFlattenable* obj, PaintFlats paintflat) {
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/**
|
|
||||||
* If SkBitmaps are to be flattened to send to the reader, this class is
|
|
||||||
* provided to the SkBitmapHeap to tell the SkGPipeCanvas to do so.
|
|
||||||
*/
|
|
||||||
class BitmapShuttle : public SkBitmapHeap::ExternalStorage {
|
|
||||||
public:
|
|
||||||
BitmapShuttle(SkGPipeCanvas*);
|
|
||||||
|
|
||||||
~BitmapShuttle();
|
|
||||||
|
|
||||||
virtual bool insert(const SkBitmap& bitmap, int32_t slot) SK_OVERRIDE;
|
|
||||||
|
|
||||||
private:
|
|
||||||
SkGPipeCanvas* fCanvas;
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#define MIN_BLOCK_SIZE (16 * 1024)
|
#define MIN_BLOCK_SIZE (16 * 1024)
|
||||||
#define BITMAPS_TO_KEEP 5
|
#define BITMAPS_TO_KEEP 5
|
||||||
#define FLATTENABLES_TO_KEEP 10
|
#define FLATTENABLES_TO_KEEP 10
|
||||||
@ -440,9 +455,8 @@ SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shouldFlattenBitmaps(flags)) {
|
if (shouldFlattenBitmaps(flags)) {
|
||||||
BitmapShuttle* shuttle = SkNEW_ARGS(BitmapShuttle, (this));
|
fBitmapShuttle.reset(SkNEW_ARGS(BitmapShuttle, (this)));
|
||||||
fBitmapHeap = SkNEW_ARGS(SkBitmapHeap, (shuttle, BITMAPS_TO_KEEP));
|
fBitmapHeap = SkNEW_ARGS(SkBitmapHeap, (fBitmapShuttle.get(), BITMAPS_TO_KEEP));
|
||||||
shuttle->unref();
|
|
||||||
} else {
|
} else {
|
||||||
fBitmapHeap = SkNEW_ARGS(SkBitmapHeap,
|
fBitmapHeap = SkNEW_ARGS(SkBitmapHeap,
|
||||||
(BITMAPS_TO_KEEP, controller->numberOfReaders()));
|
(BITMAPS_TO_KEEP, controller->numberOfReaders()));
|
||||||
@ -456,7 +470,7 @@ SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller,
|
|||||||
}
|
}
|
||||||
|
|
||||||
SkGPipeCanvas::~SkGPipeCanvas() {
|
SkGPipeCanvas::~SkGPipeCanvas() {
|
||||||
this->finish();
|
this->finish(true);
|
||||||
SkSafeUnref(fFactorySet);
|
SkSafeUnref(fFactorySet);
|
||||||
SkSafeUnref(fBitmapHeap);
|
SkSafeUnref(fBitmapHeap);
|
||||||
}
|
}
|
||||||
@ -474,7 +488,8 @@ bool SkGPipeCanvas::needOpBytes(size_t needed) {
|
|||||||
size_t blockSize = SkMax32(MIN_BLOCK_SIZE, needed);
|
size_t blockSize = SkMax32(MIN_BLOCK_SIZE, needed);
|
||||||
void* block = fController->requestBlock(blockSize, &fBlockSize);
|
void* block = fController->requestBlock(blockSize, &fBlockSize);
|
||||||
if (NULL == block) {
|
if (NULL == block) {
|
||||||
fDone = true;
|
// Do not notify the readers, which would call this function again.
|
||||||
|
this->finish(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SkASSERT(SkIsAlign4(fBlockSize));
|
SkASSERT(SkIsAlign4(fBlockSize));
|
||||||
@ -1179,7 +1194,7 @@ SkCanvas* SkGPipeWriter::startRecording(SkGPipeController* controller, uint32_t
|
|||||||
|
|
||||||
void SkGPipeWriter::endRecording() {
|
void SkGPipeWriter::endRecording() {
|
||||||
if (fCanvas) {
|
if (fCanvas) {
|
||||||
fCanvas->finish();
|
fCanvas->finish(true);
|
||||||
fCanvas->unref();
|
fCanvas->unref();
|
||||||
fCanvas = NULL;
|
fCanvas = NULL;
|
||||||
}
|
}
|
||||||
@ -1211,9 +1226,18 @@ BitmapShuttle::BitmapShuttle(SkGPipeCanvas* canvas) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BitmapShuttle::~BitmapShuttle() {
|
BitmapShuttle::~BitmapShuttle() {
|
||||||
fCanvas->unref();
|
this->removeCanvas();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BitmapShuttle::insert(const SkBitmap& bitmap, int32_t slot) {
|
bool BitmapShuttle::insert(const SkBitmap& bitmap, int32_t slot) {
|
||||||
|
SkASSERT(fCanvas != NULL);
|
||||||
return fCanvas->shuttleBitmap(bitmap, slot);
|
return fCanvas->shuttleBitmap(bitmap, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BitmapShuttle::removeCanvas() {
|
||||||
|
if (NULL == fCanvas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fCanvas->unref();
|
||||||
|
fCanvas = NULL;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user