Subject: bug#71929: 30.0.60; crash in mark_image_cache

From: Eli Zaretskii <[email-masked]>
Date: Fri, 05 Jul 2024 10:37:23 +0300

> From: Po Lu <[email-masked]>
> Cc: Sean Whitton <[email-masked]>,  [email-masked]
> Date: Fri, 05 Jul 2024 14:41:31 +0800
> 
> Eli Zaretskii <[email-masked]> writes:
> 
> >> From: Sean Whitton <[email-masked]>
> >> Cc: [email-masked]
> >> Date: Fri, 05 Jul 2024 08:13:14 +0800
> >> 
> >> Hello,
> >> 
> >> On Thu 04 Jul 2024 at 09:03am +03, Eli Zaretskii wrote:
> >> 
> >> >> What is the value of c->images?  IOW, why did this line segfault?
> >> >
> >> > Also, what is the value of c->refcount?
> >> 
> >> (gdb) p c
> >> $1 = (struct image_cache *) 0x555557c89e20
> >> (gdb) xpr
> >> There is no member named i.
> >> 
> >> (gdb) p c->images
> >> $2 = (struct image **) 0x35
> >> (gdb) xpr
> >> Cannot access memory at address 0x35
> >> 
> >> (gdb) p c->refcount
> >> $4 = 93823560581177
> >
> > So it's garbled.
> >
> > Po Lu, how do we handle the "shared" image cache when a frame is
> > deleted?  Where's the code which frees the cache if the cache's
> > refcount is one when the frame is deleted?
> 
> There's only one caller of free_image_cache, free_frame_faces, which is
> only called once in a frame's existence.  Cache refcounts are also
> altered from gui_set_font, but this process never entails decrementing a
> refcount to zero, as the caches under consideration are always retained
> by one or more frames:
> 
>   iwidth = max (10, FRAME_COLUMN_WIDTH (f));
>   if (FRAME_IMAGE_CACHE (f)
>       && (iwidth != FRAME_IMAGE_CACHE (f)->scaling_col_width))
>     {
>       eassert (FRAME_IMAGE_CACHE (f)->refcount >= 1);
>       if (FRAME_IMAGE_CACHE (f)->refcount == 1)
> 	{
> 	  /* This frame is the only user of this image cache.  */
> 	  FRAME_IMAGE_CACHE (f)->scaling_col_width = iwidth;
> 	  /* Clean F's image cache of images whose values are derived
> 	     from the font width.  */
> 	  clear_image_cache (f, Qauto);
> 	}
>       else
> 	{
> 	  /* Release the current image cache, and reuse or allocate a
> 	     new image cache with IWIDTH.  */
> 	  FRAME_IMAGE_CACHE (f)->refcount--;
> 	  FRAME_IMAGE_CACHE (f) = share_image_cache (f);
> 	  FRAME_IMAGE_CACHE (f)->refcount++;
> 	}
>     }

How does this answer my question?

The use case I was thinking of is that the image cache was shared,
then the last frame which shared the cache was deleted.  How do we
make sure the cache is freed and set to NULL in this situation?

IOW, we seem to have a cache that is not NULL but is also not a real
cache, as it cannot be accessed.  The question is how did that happen?