mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-27 19:22:49 +01:00
internal/atlas: bug fix: a finalizer was never called
As the finalizer function had a reference to the target image, the image's reference count never became 0. Then, the image was never finalized. This change fixes this issue by using a member function pointer instead of an anonymous function. Closes #2897
This commit is contained in:
parent
48f3d43189
commit
9a8dde6503
@ -67,3 +67,9 @@ func (i *Image) EnsureIsolatedFromSourceForTesting(backends []*backend) {
|
|||||||
var FlushDeferredForTesting = flushDeferred
|
var FlushDeferredForTesting = flushDeferred
|
||||||
|
|
||||||
var FloorPowerOf2 = floorPowerOf2
|
var FloorPowerOf2 = floorPowerOf2
|
||||||
|
|
||||||
|
func DeferredFuncCountForTesting() int {
|
||||||
|
deferredM.Lock()
|
||||||
|
defer deferredM.Unlock()
|
||||||
|
return len(deferred)
|
||||||
|
}
|
||||||
|
@ -715,6 +715,15 @@ func (i *Image) canBePutOnAtlas() bool {
|
|||||||
return i.width+i.paddingSize() <= maxSize && i.height+i.paddingSize() <= maxSize
|
return i.width+i.paddingSize() <= maxSize && i.height+i.paddingSize() <= maxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Image) finalize() {
|
||||||
|
// A function from finalizer must not be blocked, but disposing operation can be blocked.
|
||||||
|
// Defer this operation until it becomes safe. (#913)
|
||||||
|
appendDeferred(func() {
|
||||||
|
i.deallocate()
|
||||||
|
runtime.SetFinalizer(i, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) {
|
func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) {
|
||||||
if !graphicsDriverInitialized {
|
if !graphicsDriverInitialized {
|
||||||
panic("atlas: graphics driver must be ready at allocate but not")
|
panic("atlas: graphics driver must be ready at allocate but not")
|
||||||
@ -724,14 +733,7 @@ func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) {
|
|||||||
panic("atlas: the image is already allocated")
|
panic("atlas: the image is already allocated")
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime.SetFinalizer(i, func(image *Image) {
|
runtime.SetFinalizer(i, (*Image).finalize)
|
||||||
// A function from finalizer must not be blocked, but disposing operation can be blocked.
|
|
||||||
// Defer this operation until it becomes safe. (#913)
|
|
||||||
appendDeferred(func() {
|
|
||||||
image.deallocate()
|
|
||||||
runtime.SetFinalizer(i, nil)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
if i.imageType == ImageTypeScreen {
|
if i.imageType == ImageTypeScreen {
|
||||||
if asSource {
|
if asSource {
|
||||||
|
@ -810,4 +810,17 @@ func TestIteratingImagesToPutOnSourceBackend(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGC(t *testing.T) {
|
||||||
|
c0 := atlas.DeferredFuncCountForTesting()
|
||||||
|
img := atlas.NewImage(16, 16, atlas.ImageTypeRegular)
|
||||||
|
img.WritePixels(make([]byte, 4*16*16), image.Rect(0, 0, 16, 16))
|
||||||
|
_ = img
|
||||||
|
runtime.GC()
|
||||||
|
|
||||||
|
c1 := atlas.DeferredFuncCountForTesting()
|
||||||
|
if got, want := c1, c0+1; got != want {
|
||||||
|
t.Errorf("got: %d, want: %d", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Add tests to extend image on an atlas out of the main loop
|
// TODO: Add tests to extend image on an atlas out of the main loop
|
||||||
|
Loading…
Reference in New Issue
Block a user