Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decref an object because cython increfed it [memory leak] #2665

Merged

Conversation

Matiiss
Copy link
Member

@Matiiss Matiiss commented Jan 13, 2024

MRE:

import pygame
import pygame._sdl2 as pg_sdl2  # noqa

window = pygame.Window(minimized=True)
renderer = pg_sdl2.Renderer(window)

while not pygame.event.get(pygame.QUIT):
    renderer.to_surface()

Basically casting to object in cython increments the reference count, but pgSurface_New2 already returns an object with an incremented reference count, so have to decrement what cython has added on top of that.

@Matiiss Matiiss added the _sdl2 pygame._sdl2 label Jan 13, 2024
@Matiiss Matiiss requested a review from a team as a code owner January 13, 2024 01:05
@Matiiss Matiiss changed the title Decref an object because cython increfed it Decref an object because cython increfed it [memory leak] Jan 13, 2024
@@ -1233,6 +1234,7 @@ cdef class Renderer:
raise MemoryError("not enough memory for the surface")

surface = <object>pgSurface_New2(surf, 1)
Py_DECREF(surface)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this should be considered a Cython bug and reported? Or is this a known and documented thing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I stumbled across similar concerns while searching for an answer, but everyone said that it's intended behavior, perchance, because something like <object>1 would create a Python int, for example (so, need to increment the reference count).
Anyway, some relevant bits I found:
https://stackoverflow.com/a/32562120/14531062
https://stackoverflow.com/a/56686757/14531062
https://cython.readthedocs.io/en/latest/src/userguide/language_basics.html?highlight=%22except%20%20%22#python-objects-as-parameters-and-return-values
cython/cython#2589

So yeah, seems to be expected and I guess it does make sense if the usual usecase is to directly go from C structs and stuff to Python objects.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So there must a memory leak, but I don't think it's due to the refcount being wrong. I just passed the return value of the function to sys.getrefcount, which returns the expected value.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were you doing

surf = renderer.to_surface()
print(sys.getrefcount(surf))

or directly? Because that returns two different results.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was doing it directly, and I got a 2, which I thought is expected, but actually I was wrong. So yeah, this fix is correct.

Going through one of the links you posted, I see an alternate fix involves changing the declaration itself to avoid the cast, and IMO that is a neater solution

Copy link
Member Author

@Matiiss Matiiss Jan 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no clue how to implement that alternate fix which I assume you meant declaring surface to be a PyObject pointer and thus it gets passed by reference, but then I'd think you need to incref it if the surface is actually passed since it wouldn't get increfed automatically and I'm not even entirely sure how that mechanic works there, the current fix seems simple enough though, it's only 2 lines after all and quite familiar usage of the C API.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I was thinking, is replacing pgSurfaceObject *pgSurface_New2(SDL_Surface *info, int owner) with object pgSurface_New2(SDL_Surface *info, int owner) in video.pxd and then the cast could be removed easily

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, that also appears to have fixed the memory leak, much simpler as well 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slight issue, this is so far failing on emscripten due to incompatible pointer types
https://github.com/pygame-community/pygame-ce/actions/runs/7533032001/job/20504804242?pr=2665#step:7:201

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrmmm, not sure why this is happening, -Werror is supposed to be ignored on cython files

Copy link
Member

@ankith26 ankith26 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for the PR 🎉

Left a review for your consideration, but this PR is mergeable in its current state too. Maybe we could squash and merge this PR because the first commit is huge and the second does a lot of reverting

PS: please ignore my alt's reviews, I forgor to switch accounts 💀

@Matiiss Matiiss force-pushed the matiiss-fix-Renderer.to_surface branch 2 times, most recently from 5e1e506 to 405ee1a Compare January 14, 2024 19:44
@Matiiss Matiiss added the bugfix PR that fixes bug label Jan 14, 2024
Changed cython version to minimize diff

Tests for good measure

Added a comment to clarify DECREF usage
@Matiiss Matiiss force-pushed the matiiss-fix-Renderer.to_surface branch from 9b2d8c0 to 61ea93e Compare January 15, 2024 12:20
@Starbuck5
Copy link
Member

The web build is failing here.

src_c/_sdl2/video.c:20531:15: error: incompatible pointer types assigning to 'PyObject *' (aka 'struct _object *') from 'pgSurfaceObject *' [-Werror,-Wincompatible-pointer-types]
    __pyx_t_2 = pgSurface_New2(__pyx_v_surf, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1235, __pyx_L1_error)
              ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

@Matiiss
Copy link
Member Author

Matiiss commented Jan 17, 2024

The web build is failing here.

src_c/_sdl2/video.c:20531:15: error: incompatible pointer types assigning to 'PyObject *' (aka 'struct _object *') from 'pgSurfaceObject *' [-Werror,-Wincompatible-pointer-types]
    __pyx_t_2 = pgSurface_New2(__pyx_v_surf, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1235, __pyx_L1_error)
              ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

Yes, already mentioned that to Ankith in that review comment, not sure what to do though.

@Matiiss
Copy link
Member Author

Matiiss commented Jan 19, 2024

Reverted back to using Py_DECREF so that it passes tests. I think it's fine to leave it at this since the cython code won't be around forever anyway, so in a sense this is a temporary fix either way.
Of course open to suggestions regarding the WASM error.

Copy link
Member

@ankith26 ankith26 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay looks like the alternate fix isn't passing WASM CI even after trying some workarounds. Let's go ahead with this PR, thanks! 👍

PS: let's squash and merge this PR

Copy link
Member

@MyreMylar MyreMylar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@MyreMylar MyreMylar merged commit 10e86e8 into pygame-community:main Jan 28, 2024
30 checks passed
@ankith26 ankith26 added this to the 2.5.0 milestone Jan 29, 2024
Starbuck5 pushed a commit that referenced this pull request Feb 18, 2024
* Decref an object because cython increfed it
Tests for good measure
Added a comment to clarify DECREF usage
Skip test on pypy
@Starbuck5 Starbuck5 modified the milestones: 2.5.0, 2.4.1 Feb 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugfix PR that fixes bug _sdl2 pygame._sdl2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants