OSX/MACOS/COCOATOUCH: Normalise MRR safety in cocoa_gl_ctx.m

Audit of the OpenGL context driver turned up three latent MRR
leaks, all on the iOS/tvOS side where the code relies on ARC's
strong-store semantics for release-on-reassignment.  Every
shipping iOS build currently enables ARC, so none of these leak
in practice today; making the paths MRR-safe as well brings the
driver in line with cocoa_common.m / ui_cocoa.m (which compile
under both ARC and MRR via the RARCH_*/RELEASE macro family) and
keeps the qb/make-style non-Xcode build viable for COCOATOUCH
targets.

All three fixes use the existing RELEASE macro from
cocoa_common.h, which under ARC expands to plain `x = nil` and
under MRR to `[x release]; x = nil` - so this is a no-op under
ARC and a strict correctness improvement under MRR.

- cocoa_gl_gfx_ctx_destroy: previously only the OSX branch called
  RELEASE on g_ctx / g_hw_ctx; the iOS branch dropped the +1 from
  [[EAGLContext alloc] initWithAPI:...] with a raw `g_ctx = nil`.
  Move the RELEASE calls unconditional (post-#endif) and add
  RELEASE(glk_view) under #if defined(HAVE_COCOATOUCH) so the
  GLKView created in glkitview_init does not leak across
  init/destroy cycles even on clean shutdown.

- cocoa_gl_gfx_ctx_set_video_mode (both branches): add defensive
  RELEASE(g_ctx) / RELEASE(g_hw_ctx) before the
  [[NSOpenGLContext alloc] ...] / [[EAGLContext alloc] ...]
  overwrites.  The normal reinit flow has -destroy running first
  so both statics are already nil here, but guarding defensively
  means a future re-init-without-destroy call site cannot leak
  the previous +1.

- glkitview_init: release the previous glk_view before
  overwriting with [GLKView new].  The existing
  [glk_view deleteDrawable] handled the GL-side cleanup but left
  the Obj-C retain dangling under MRR.

cocoa_vk_ctx.m has no static Obj-C pointers and needed no
changes.  MoltenVK / vulkan_context_destroy own the Vulkan
surface / device lifetime, and all CocoaView accesses there are
brief unretained reads backed by cocoa_common.m's g_instance
strong reference (see 0062608).
This commit is contained in:
U-DESKTOP-SPFP6AQ\twistedtechre committed 2026-04-21 19:11:22 +02:00
1 parent 0062608a13
commit bd45086237
1 file changed
+34 -4
+34 -4
View File
@@ -144,6 +144,11 @@ void *glkitview_init(void)
* glPushGroupMarkerEXT with no current context and crashes. */
if (glk_view)
[glk_view deleteDrawable];
/* RELEASE the old view (+1 from the previous [GLKView new]) before
* overwriting the static. Under ARC the strong static would retain/
* release on store, but under MRR the raw assignment below would drop
* the +1 and leak one GLKView per re-init. */
RELEASE(glk_view);
glk_view = [GLKView new];
#if TARGET_OS_IOS
@@ -172,10 +177,8 @@ static void cocoa_gl_gfx_ctx_destroy(void *data)
#ifdef OSX
[GLContextClass clearCurrentContext];
[g_ctx clearDrawable];
RELEASE(g_ctx);
if (g_hw_ctx)
[g_hw_ctx clearDrawable];
RELEASE(g_hw_ctx);
[GLContextClass clearCurrentContext];
#else
/* Clean up GLKView's framebuffer resources while context is still valid.
@@ -185,8 +188,18 @@ static void cocoa_gl_gfx_ctx_destroy(void *data)
[glk_view deleteDrawable];
[EAGLContext setCurrentContext:nil];
#endif
g_hw_ctx = nil;
g_ctx = nil;
/* RELEASE -releases + nils under MRR, just nils under ARC (see
* cocoa_common.h). Doing this unconditionally matches the OSX
* path's previous behaviour and fixes a latent iOS-MRR leak where
* the +1 from [[EAGLContext alloc] initWithAPI:...] in set_video_mode
* was dropped with a raw 'g_ctx = nil'. */
RELEASE(g_ctx);
RELEASE(g_hw_ctx);
#if defined(HAVE_COCOATOUCH)
/* glk_view was created with +new (+1) in glkitview_init. Release
* it on destroy so it does not leak across init/destroy cycles. */
RELEASE(glk_view);
#endif
free(cocoa_ctx);
}
@@ -428,11 +441,21 @@ static bool cocoa_gl_gfx_ctx_set_video_mode(void *data,
if (cocoa_ctx->flags & COCOA_CTX_FLAG_USE_HW_CTX)
{
/* In the normal reinit flow -destroy runs before -set_video_mode
* and both statics are already nil here; guard defensively so an
* MRR build does not leak the previous +1 if that invariant ever
* breaks (e.g. a future caller that re-inits without tearing
* down first). Safe when already nil under both ARC and MRR. */
RELEASE(g_ctx);
RELEASE(g_hw_ctx);
g_hw_ctx = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:nil];
g_ctx = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:g_hw_ctx];
}
else
{
RELEASE(g_ctx);
g_ctx = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:nil];
}
RELEASE(fmt);
}
@@ -611,6 +634,13 @@ static bool cocoa_gl_gfx_ctx_set_video_mode(void *data,
{
cocoa_ctx_data_t *cocoa_ctx = (cocoa_ctx_data_t*)data;
/* In the normal reinit flow -destroy runs before -set_video_mode and
* both statics are already nil here; guard defensively so an iOS-MRR
* build does not leak the previous +1 if that invariant ever breaks
* (e.g. a future caller that re-inits without tearing down first). */
RELEASE(g_ctx);
RELEASE(g_hw_ctx);
#if defined(HAVE_OPENGLES3)
if (cocoa_ctx->flags & COCOA_CTX_FLAG_USE_HW_CTX)
{